'  
'   Rebex Sample Code License
' 
'   Copyright 2025, 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.IO
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Imports System.Threading

Imports Rebex.IO
Imports Rebex.Net
Imports Rebex

''' <summary>
''' Main application form.
''' </summary>
Public Class MainForm
    Inherits System.Windows.Forms.Form

    ' Path to configuration file
    Public Shared ReadOnly ConfigFilePath As String = Path.Combine(Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), String.Format("Rebex{0}FTP SSL{0}BatchTransfer.xml", Path.DirectorySeparatorChar))

    ' Application configuration
    Private ReadOnly _config As Configuration

    Private _ftp As Ftp
    Private _openFolder As FolderBrowserDialog

#Region "Windows Form Designer generated code"
    Public Sub New()
            ' set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key


        CheckForIllegalCrossThreadCalls = True

        InitializeComponent()

        _config = New Configuration(ConfigFilePath)
        _openFolder = New FolderBrowserDialog
        _problemForm = New ProblemDetectedForm
        lblFilesCount.Text = String.Empty

        LoadSettings()
    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides 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

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Private WithEvents label5 As System.Windows.Forms.Label
    Private WithEvents label6 As System.Windows.Forms.Label
    Private WithEvents tbPassword As System.Windows.Forms.TextBox
    Private WithEvents label4 As System.Windows.Forms.Label
    Private WithEvents tbUserName As System.Windows.Forms.TextBox
    Private WithEvents label3 As System.Windows.Forms.Label
    Private WithEvents tbPort As System.Windows.Forms.TextBox
    Private WithEvents label2 As System.Windows.Forms.Label
    Private WithEvents tbHostname As System.Windows.Forms.TextBox
    Private WithEvents label1 As System.Windows.Forms.Label
    Private WithEvents tbLocalPath As System.Windows.Forms.TextBox
    Private WithEvents tbRemotePath As System.Windows.Forms.TextBox
    Private WithEvents label7 As System.Windows.Forms.Label
    Private WithEvents lblFilesCount As System.Windows.Forms.Label
    Private WithEvents pbFile As System.Windows.Forms.ProgressBar
    Private WithEvents pbTotal As System.Windows.Forms.ProgressBar
    Private WithEvents btnUpload As System.Windows.Forms.Button
    Private WithEvents btnDownload As System.Windows.Forms.Button
    Private WithEvents btnOpen As System.Windows.Forms.Button
    Private WithEvents label8 As System.Windows.Forms.Label
    Private WithEvents lblBytesTotal As System.Windows.Forms.Label
    Private WithEvents statusStrip As System.Windows.Forms.StatusStrip
    Private WithEvents statusLabel As System.Windows.Forms.ToolStripStatusLabel
    Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
    Private WithEvents panel1 As System.Windows.Forms.Panel
    Private WithEvents label9 As System.Windows.Forms.Label
    Private WithEvents label10 As System.Windows.Forms.Label
    Private WithEvents lblFrom As System.Windows.Forms.Label
    Private WithEvents lblTo As System.Windows.Forms.Label
    Private WithEvents lblToPath As System.Windows.Forms.Label
    Private WithEvents lblFromPath As System.Windows.Forms.Label
    Private WithEvents btnAbort As System.Windows.Forms.Button
    Private WithEvents _problemForm As ProblemDetectedForm
    Private WithEvents cbSecurity As System.Windows.Forms.ComboBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.tbLocalPath = New System.Windows.Forms.TextBox
        Me.label5 = New System.Windows.Forms.Label
        Me.tbRemotePath = New System.Windows.Forms.TextBox
        Me.label6 = New System.Windows.Forms.Label
        Me.tbPassword = New System.Windows.Forms.TextBox
        Me.label4 = New System.Windows.Forms.Label
        Me.tbUserName = New System.Windows.Forms.TextBox
        Me.label3 = New System.Windows.Forms.Label
        Me.tbPort = New System.Windows.Forms.TextBox
        Me.label2 = New System.Windows.Forms.Label
        Me.tbHostname = New System.Windows.Forms.TextBox
        Me.label1 = New System.Windows.Forms.Label
        Me.btnOpen = New System.Windows.Forms.Button
        Me.label7 = New System.Windows.Forms.Label
        Me.lblFilesCount = New System.Windows.Forms.Label
        Me.lblFrom = New System.Windows.Forms.Label
        Me.lblTo = New System.Windows.Forms.Label
        Me.pbFile = New System.Windows.Forms.ProgressBar
        Me.pbTotal = New System.Windows.Forms.ProgressBar
        Me.btnUpload = New System.Windows.Forms.Button
        Me.btnDownload = New System.Windows.Forms.Button
        Me.label8 = New System.Windows.Forms.Label
        Me.lblBytesTotal = New System.Windows.Forms.Label
        Me.statusStrip = New System.Windows.Forms.StatusStrip
        Me.statusLabel = New System.Windows.Forms.ToolStripStatusLabel
        Me.groupBox1 = New System.Windows.Forms.GroupBox
        Me.cbSecurity = New System.Windows.Forms.ComboBox
        Me.panel1 = New System.Windows.Forms.Panel
        Me.label10 = New System.Windows.Forms.Label
        Me.label9 = New System.Windows.Forms.Label
        Me.lblToPath = New System.Windows.Forms.Label
        Me.lblFromPath = New System.Windows.Forms.Label
        Me.btnAbort = New System.Windows.Forms.Button
        Me.groupBox1.SuspendLayout()
        Me.panel1.SuspendLayout()
        Me.statusStrip.SuspendLayout()
        Me.SuspendLayout()
        '
        'tbLocalPath
        '
        Me.tbLocalPath.Location = New System.Drawing.Point(104, 72)
        Me.tbLocalPath.Name = "tbLocalPath"
        Me.tbLocalPath.Size = New System.Drawing.Size(272, 20)
        Me.tbLocalPath.TabIndex = 5
        Me.tbLocalPath.Text = ""
        '
        'label5
        '
        Me.label5.Location = New System.Drawing.Point(8, 72)
        Me.label5.Name = "label5"
        Me.label5.Size = New System.Drawing.Size(96, 20)
        Me.label5.TabIndex = 21
        Me.label5.Text = "Local path:"
        Me.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'tbRemotePath
        '
        Me.tbRemotePath.Location = New System.Drawing.Point(104, 96)
        Me.tbRemotePath.Name = "tbRemotePath"
        Me.tbRemotePath.Size = New System.Drawing.Size(352, 20)
        Me.tbRemotePath.TabIndex = 7
        Me.tbRemotePath.Text = ""
        '
        'label6
        '
        Me.label6.Location = New System.Drawing.Point(8, 96)
        Me.label6.Name = "label6"
        Me.label6.Size = New System.Drawing.Size(96, 20)
        Me.label6.TabIndex = 23
        Me.label6.Text = "Remote path:"
        Me.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'tbPassword
        '
        Me.tbPassword.Location = New System.Drawing.Point(320, 48)
        Me.tbPassword.Name = "tbPassword"
        Me.tbPassword.PasswordChar = Microsoft.VisualBasic.ChrW(42)
        Me.tbPassword.Size = New System.Drawing.Size(128, 20)
        Me.tbPassword.TabIndex = 4
        Me.tbPassword.Text = ""
        '
        'label4
        '
        Me.label4.Location = New System.Drawing.Point(256, 48)
        Me.label4.Name = "label4"
        Me.label4.Size = New System.Drawing.Size(64, 20)
        Me.label4.TabIndex = 31
        Me.label4.Text = "Password:"
        Me.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'tbUserName
        '
        Me.tbUserName.Location = New System.Drawing.Point(104, 48)
        Me.tbUserName.Name = "tbUserName"
        Me.tbUserName.Size = New System.Drawing.Size(144, 20)
        Me.tbUserName.TabIndex = 3
        Me.tbUserName.Text = ""
        '
        'label3
        '
        Me.label3.Location = New System.Drawing.Point(8, 48)
        Me.label3.Name = "label3"
        Me.label3.Size = New System.Drawing.Size(96, 20)
        Me.label3.TabIndex = 30
        Me.label3.Text = "Login name:"
        Me.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'tbPort
        '
        Me.tbPort.Location = New System.Drawing.Point(320, 24)
        Me.tbPort.Name = "tbPort"
        Me.tbPort.Size = New System.Drawing.Size(32, 20)
        Me.tbPort.TabIndex = 1
        Me.tbPort.Text = "22"
        '
        'label2
        '
        Me.label2.Location = New System.Drawing.Point(256, 24)
        Me.label2.Name = "label2"
        Me.label2.Size = New System.Drawing.Size(64, 20)
        Me.label2.TabIndex = 29
        Me.label2.Text = "Port:"
        Me.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'tbHostname
        '
        Me.tbHostname.Location = New System.Drawing.Point(104, 24)
        Me.tbHostname.Name = "tbHostname"
        Me.tbHostname.Size = New System.Drawing.Size(144, 20)
        Me.tbHostname.TabIndex = 0
        Me.tbHostname.Text = ""
        '
        'label1
        '
        Me.label1.Location = New System.Drawing.Point(8, 24)
        Me.label1.Name = "label1"
        Me.label1.Size = New System.Drawing.Size(96, 20)
        Me.label1.TabIndex = 28
        Me.label1.Text = "Host name:"
        Me.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'btnOpen
        '
        Me.btnOpen.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnOpen.Location = New System.Drawing.Point(384, 72)
        Me.btnOpen.Name = "btnOpen"
        Me.btnOpen.Size = New System.Drawing.Size(72, 20)
        Me.btnOpen.TabIndex = 6
        Me.btnOpen.Text = "&Browse..."
        '
        'label7
        '
        Me.label7.Location = New System.Drawing.Point(8, 280)
        Me.label7.Name = "label7"
        Me.label7.Size = New System.Drawing.Size(120, 24)
        Me.label7.TabIndex = 33
        Me.label7.Text = "Files processed / total:"
        '
        'lblFilesCount
        '
        Me.lblFilesCount.Location = New System.Drawing.Point(128, 280)
        Me.lblFilesCount.Name = "lblFilesCount"
        Me.lblFilesCount.Size = New System.Drawing.Size(128, 24)
        Me.lblFilesCount.TabIndex = 34
        Me.lblFilesCount.Text = "{0} / {1}"
        '
        'lblFrom
        '
        Me.lblFrom.Location = New System.Drawing.Point(8, 224)
        Me.lblFrom.Name = "lblFrom"
        Me.lblFrom.Size = New System.Drawing.Size(48, 24)
        Me.lblFrom.TabIndex = 36
        Me.lblFrom.Text = "From:"
        '
        'lblTo
        '
        Me.lblTo.Location = New System.Drawing.Point(8, 248)
        Me.lblTo.Name = "lblTo"
        Me.lblTo.Size = New System.Drawing.Size(48, 24)
        Me.lblTo.TabIndex = 37
        Me.lblTo.Text = "To:"
        '
        'pbFile
        '
        Me.pbFile.Location = New System.Drawing.Point(8, 304)
        Me.pbFile.Name = "pbFile"
        Me.pbFile.Size = New System.Drawing.Size(492, 23)
        Me.pbFile.TabIndex = 38
        '
        'pbTotal
        '
        Me.pbTotal.Location = New System.Drawing.Point(8, 336)
        Me.pbTotal.Name = "pbTotal"
        Me.pbTotal.Size = New System.Drawing.Size(492, 23)
        Me.pbTotal.TabIndex = 39
        '
        'btnUpload
        '
        Me.btnUpload.Location = New System.Drawing.Point(344, 192)
        Me.btnUpload.Name = "btnUpload"
        Me.btnUpload.TabIndex = 9
        Me.btnUpload.Text = "&Upload"
        '
        'btnDownload
        '
        Me.btnDownload.Location = New System.Drawing.Point(264, 192)
        Me.btnDownload.Name = "btnDownload"
        Me.btnDownload.TabIndex = 8
        Me.btnDownload.Text = "&Download"
        '
        'label8
        '
        Me.label8.Location = New System.Drawing.Point(272, 280)
        Me.label8.Name = "label8"
        Me.label8.Size = New System.Drawing.Size(80, 24)
        Me.label8.TabIndex = 42
        Me.label8.Text = "Bytes total:"
        '
        'lblBytesTotal
        '
        Me.lblBytesTotal.Location = New System.Drawing.Point(360, 280)
        Me.lblBytesTotal.Name = "lblBytesTotal"
        Me.lblBytesTotal.Size = New System.Drawing.Size(144, 24)
        Me.lblBytesTotal.TabIndex = 43
        Me.lblBytesTotal.Text = "0"
        '
        'statusStrip
        '
        Me.statusStrip.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.statusLabel})
        Me.statusStrip.Location = New System.Drawing.Point(0, 374)
        Me.statusStrip.Name = "statusStrip"
        Me.statusStrip.Size = New System.Drawing.Size(506, 22)
        Me.statusStrip.SizingGrip = False
        Me.statusStrip.TabIndex = 44
        '
        'statusLabel
        '
        Me.statusLabel.Name = "statusLabel"
        Me.statusLabel.Size = New System.Drawing.Size(90, 21)
        Me.statusLabel.Text = "Welcome!"
        '
        'groupBox1
        '
        Me.groupBox1.Controls.Add(Me.cbSecurity)
        Me.groupBox1.Controls.Add(Me.tbPort)
        Me.groupBox1.Controls.Add(Me.tbPassword)
        Me.groupBox1.Controls.Add(Me.label6)
        Me.groupBox1.Controls.Add(Me.label2)
        Me.groupBox1.Controls.Add(Me.tbRemotePath)
        Me.groupBox1.Controls.Add(Me.tbHostname)
        Me.groupBox1.Controls.Add(Me.label5)
        Me.groupBox1.Controls.Add(Me.tbLocalPath)
        Me.groupBox1.Controls.Add(Me.btnOpen)
        Me.groupBox1.Controls.Add(Me.label1)
        Me.groupBox1.Controls.Add(Me.label4)
        Me.groupBox1.Controls.Add(Me.tbUserName)
        Me.groupBox1.Controls.Add(Me.label3)
        Me.groupBox1.Location = New System.Drawing.Point(4, 60)
        Me.groupBox1.Name = "groupBox1"
        Me.groupBox1.Size = New System.Drawing.Size(498, 128)
        Me.groupBox1.TabIndex = 45
        Me.groupBox1.TabStop = False
        Me.groupBox1.Text = "Settings"
        '
        'cbSecurity
        '
        Me.cbSecurity.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
        Me.cbSecurity.Items.AddRange(New Object() {"No security", "Explicit TLS/SSL", "Implicit TLS/SSL"})
        Me.cbSecurity.Location = New System.Drawing.Point(360, 24)
        Me.cbSecurity.MaxDropDownItems = 3
        Me.cbSecurity.Name = "cbSecurity"
        Me.cbSecurity.Size = New System.Drawing.Size(132, 21)
        Me.cbSecurity.TabIndex = 2
        '
        'panel1
        '
        Me.panel1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
           Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.panel1.BackColor = System.Drawing.Color.White
        Me.panel1.Controls.Add(Me.label10)
        Me.panel1.Controls.Add(Me.label9)
        Me.panel1.Location = New System.Drawing.Point(0, 0)
        Me.panel1.Name = "panel1"
        Me.panel1.Size = New System.Drawing.Size(506, 56)
        Me.panel1.TabIndex = 46
        '
        'label10
        '
        Me.label10.Location = New System.Drawing.Point(16, 26)
        Me.label10.Name = "label10"
        Me.label10.Size = New System.Drawing.Size(248, 18)
        Me.label10.TabIndex = 48
        Me.label10.Text = "Uploads or downloads multiple files."
        '
        'label9
        '
        Me.label9.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(238, Byte))
        Me.label9.Location = New System.Drawing.Point(8, 8)
        Me.label9.Name = "label9"
        Me.label9.Size = New System.Drawing.Size(248, 18)
        Me.label9.TabIndex = 47
        Me.label9.Text = "Batch Transfer Utility"
        '
        'lblToPath
        '
        Me.lblToPath.Location = New System.Drawing.Point(56, 248)
        Me.lblToPath.Name = "lblToPath"
        Me.lblToPath.Size = New System.Drawing.Size(448, 24)
        Me.lblToPath.TabIndex = 48
        '
        'lblFromPath
        '
        Me.lblFromPath.Location = New System.Drawing.Point(56, 224)
        Me.lblFromPath.Name = "lblFromPath"
        Me.lblFromPath.Size = New System.Drawing.Size(448, 24)
        Me.lblFromPath.TabIndex = 47
        '
        'btnAbort
        '
        Me.btnAbort.Enabled = False
        Me.btnAbort.Location = New System.Drawing.Point(424, 192)
        Me.btnAbort.Name = "btnAbort"
        Me.btnAbort.TabIndex = 10
        Me.btnAbort.Text = "&Abort"
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(506, 396)
        Me.Controls.Add(Me.btnAbort)
        Me.Controls.Add(Me.lblToPath)
        Me.Controls.Add(Me.lblFromPath)
        Me.Controls.Add(Me.panel1)
        Me.Controls.Add(Me.groupBox1)
        Me.Controls.Add(Me.statusStrip)
        Me.Controls.Add(Me.lblBytesTotal)
        Me.Controls.Add(Me.label8)
        Me.Controls.Add(Me.btnDownload)
        Me.Controls.Add(Me.btnUpload)
        Me.Controls.Add(Me.pbTotal)
        Me.Controls.Add(Me.pbFile)
        Me.Controls.Add(Me.lblTo)
        Me.Controls.Add(Me.lblFrom)
        Me.Controls.Add(Me.lblFilesCount)
        Me.Controls.Add(Me.label7)
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
        Me.Name = "MainForm"
        Me.Text = "Rebex FTP Batch Transfer Sample"
        Me.groupBox1.ResumeLayout(False)
        Me.panel1.ResumeLayout(False)
        Me.statusStrip.ResumeLayout(False)
        Me.statusStrip.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub
#End Region

    ''' <summary>
    ''' Displays an error message.
    ''' </summary>
    ''' <param name="ex">Exception to be shown.</param>
    Private Sub ShowError(ByVal ex As Exception)
        MessageBox.Show(Me, String.Format("{0}" & vbCrLf & vbCrLf & "Exception details: {1}", ex.Message, ex), "Error", MessageBoxButtons.OK, MessageBoxIcon.[Error])
    End Sub

    ''' <summary>
    ''' Displays an error message.
    ''' </summary>
    ''' <param name="message">Message to be shown.</param>
    Private Sub ShowError(ByVal message As String)
        MessageBox.Show(Me, message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
    End Sub

    ''' <summary>
    ''' Displays a message.
    ''' </summary>
    ''' <param name="message">Message to be shown.</param>
    Private Sub ShowMessage(ByVal message As String)
        MessageBox.Show(Me, message, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    ''' <summary>
    ''' Enable/disable buttons and other controls to match the desired state
    ''' </summary>
    ''' <param name="transferring">True if transferring state is to be set; false if no transfering state is to be set.</param>
    Private Sub SetState(ByVal transferring As Boolean)
        If transferring Then
            ' enable/disable buttons
            btnDownload.Enabled = False
            btnUpload.Enabled = False
            btnAbort.Enabled = True

            ' disable connection properties
            tbHostname.Enabled = False
            tbPort.Enabled = False
            tbUserName.Enabled = False
            tbPassword.Enabled = False

            ' initialize the problem-reporting form
            _problemForm.Initialize()
        Else
            ' clear paths
            lblFromPath.Text = ""
            lblToPath.Text = ""

            ' enable / disable buttons
            btnDownload.Enabled = True
            btnUpload.Enabled = True
            btnAbort.Enabled = False

            ' enable connection properties
            tbHostname.Enabled = True
            tbPort.Enabled = True
            tbUserName.Enabled = True
            tbPassword.Enabled = True
        End If
    End Sub

    ''' <summary>
    ''' Save configuration settings.
    ''' </summary>
    Private Sub SaveSettings()
        _config.SetValue("hostname", tbHostname.Text)
        _config.SetValue("port", tbPort.Text)
        _config.SetValue("userName", tbUserName.Text)
        _config.SetValue("password", tbPassword.Text)
        _config.SetValue("localPath", tbLocalPath.Text)
        _config.SetValue("remotePath", tbRemotePath.Text)
        _config.SetValue("security", cbSecurity.SelectedIndex)
        _config.Save()
    End Sub

    ''' <summary>
    ''' Load configuration settings.
    ''' </summary>
    Private Sub LoadSettings()
        tbHostname.Text = _config.GetString("hostname", "test.rebex.net")
        tbPort.Text = _config.GetInt32("port", Ftp.DefaultPort).ToString()
        tbUserName.Text = _config.GetString("userName", "demo")
        tbPassword.Text = _config.GetString("password", "password")
        tbLocalPath.Text = _config.GetString("localPath")
        tbRemotePath.Text = _config.GetString("remotePath", "/pub/example/")
        cbSecurity.SelectedIndex = _config.GetInt32("security", 0)
    End Sub

    ''' <summary>
    ''' Connect to the FTP server.
    ''' </summary>
    ''' <returns>True if successfully connected, false otherwise.</returns>
    Private Function Connect() As Boolean
        Dim port As Integer

        ' check the hostname
        If tbHostname.Text.Length = 0 Then
            ShowError("Hostname is required.")
            Return False
        End If

        ' parse the port
        If tbPort.Text.Length = 0 Then
            port = Ftp.DefaultPort
        Else
            Integer.TryParse(tbPort.Text, port)

            ' check the port
            If port <= 0 Then
                ShowError("Port must be a positive number.")
                Return False
            End If
        End If

        Try
            Cursor = Cursors.WaitCursor

            statusLabel.Text = "Connecting..."

            Dim mode As SslMode
            Select Case cbSecurity.SelectedIndex
                Case 1
                    mode = SslMode.Explicit
                Case 2
                    mode = SslMode.Implicit
                Case Else
                    mode = SslMode.None
            End Select

            ' connect and login
            _ftp = New Ftp
            AddHandler _ftp.ValidatingCertificate, AddressOf Verifier.ValidatingCertificate
            _ftp.Connect(tbHostname.Text, port, mode)
            _ftp.Login(tbUserName.Text, tbPassword.Text)

            ' register transfer events
            AddHandler _ftp.ProblemDetected, AddressOf ftp_ProblemDetected
            AddHandler _ftp.Traversing, AddressOf ftp_Traversing
            AddHandler _ftp.TransferProgressChanged, AddressOf ftp_TransferProgressChanged

            Return True
        Catch x As Exception
            statusLabel.Text = ""
            ShowError(x)
            Return False
        Finally
            Cursor = Cursors.Arrow
        End Try

    End Function

    ''' <summary>
    ''' Disconnect from the FTP server.
    ''' </summary>
    Private Sub Disconnect()
        _ftp.Disconnect()
        _ftp.Dispose()
        _ftp = Nothing
    End Sub

    ''' <summary>
    ''' Download files.
    ''' </summary>
    ''' <param name="remotePath">Source file or directory remote path.</param>
    ''' <param name="localPath">Target directory local path.</param>
    Private Sub DownloadFiles(ByVal remotePath As String, ByVal localPath As String)
        SaveSettings()

        ' check the local path
        If localPath Is Nothing OrElse localPath.Length = 0 Then
            ShowError("Local path cannot be empty.")
            Return
        End If

        ' connect to the FTP server
        If Not Connect() Then
            Return
        End If

        Try
            SetState(True)

            ' download files
            _ftp.Download(remotePath, localPath, TraversalMode.Recursive)

            Dim message As String = "Download finished successfully."
            statusLabel.Text = message
            ShowMessage(message)

        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Operation aborted.")
            Else
                ShowError(ex)
            End If
        Catch ex As Exception
            ShowError(ex)
        Finally
            Disconnect()
            SetState(False)
        End Try
    End Sub

    ''' <summary>
    ''' Handles the Download button click event.
    ''' </summary>
    Private Sub btnDownload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownload.Click
        DownloadFiles(tbRemotePath.Text, tbLocalPath.Text)
    End Sub

    ''' <summary>
    ''' Upload files.
    ''' </summary>
    ''' <param name="localPath">Source file or directory local path.</param>
    ''' <param name="remotePath">Target directory remote path.</param>
    Private Sub UploadFiles(ByVal localPath As String, ByVal remotePath As String)
        SaveSettings()

        ' check the local path
        If localPath Is Nothing OrElse localPath.Length = 0 Then
            ShowError("Local path cannot be empty.")
            Return
        End If

        ' connect to the FTP server
        If Not Connect() Then
            Return
        End If

        Try
            SetState(True)

            ' upload files
            _ftp.Upload(localPath, remotePath, TraversalMode.Recursive)

            Dim message As String = "Upload finished successfully."
            statusLabel.Text = message
            ShowMessage(message)
        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Operation aborted.")
            Else
                ShowError(ex)
            End If
        Catch ex As Exception
            ShowError(ex)
        Finally
            Disconnect()
            SetState(False)
        End Try
    End Sub

    ''' <summary>
    ''' Handles the Upload button click event.
    ''' </summary>
    Private Sub btnUpload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpload.Click
        UploadFiles(tbLocalPath.Text, tbRemotePath.Text)
    End Sub

    ''' <summary>
    ''' Handles the Open button click event.
    ''' </summary>
    Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpen.Click
        Dim res As DialogResult = _openFolder.ShowDialog(Me)

        If res <> DialogResult.OK Then
            Return
        End If

        tbLocalPath.Text = _openFolder.SelectedPath
    End Sub

    ''' <summary>
    ''' Handles the Abort button click event.
    ''' </summary>
    Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAbort.Click
        _ftp.Abort()
    End Sub

    ''' <summary>
    ''' Handles the multi-file transfer traversing event.
    ''' </summary>
    Private Sub ftp_Traversing(sender As Object, e As FtpTraversingEventArgs)
        lblFilesCount.Text = String.Format("0 / {0}", e.FilesTotal)
        lblBytesTotal.Text = e.BytesTotal.ToString()
        lblFromPath.Text = e.Item.Path

        Select Case (e.TraversingState)
            Case TraversingState.HierarchyRetrieving
                statusLabel.Text = "Retrieving hierarchy..."
                Exit Select
            Case TraversingState.DirectoryRetrieving
                statusLabel.Text = "Retrieving directory..."
                Exit Select
            Case TraversingState.DirectoryRetrieved
                statusLabel.Text = "Directory retrieved."
                Exit Select
            Case TraversingState.HierarchyRetrieved
                statusLabel.Text = "Hierarchy retrieved."
                Exit Select
        End Select

        ' process any application events to prevent the window from freezing
        Application.DoEvents()
    End Sub

    ''' <summary>
    ''' Handles the multi-file transfer progress changed event.
    ''' </summary>
    Private Sub ftp_TransferProgressChanged(sender As Object, e As FtpTransferProgressChangedEventArgs)
        pbTotal.Value = Convert.ToInt32(e.ProgressPercentage)
        pbFile.Value = Convert.ToInt32(e.CurrentFileProgressPercentage)

        lblFilesCount.Text = String.Format("{0} / {1}", e.FilesProcessed, e.FilesTotal)

        lblFromPath.Text = e.SourceItem.Path
        lblToPath.Text = e.TargetPath

        Select Case (e.TransferState)
            Case TransferProgressState.DirectoryProcessing
                statusLabel.Text = "Processing directory..."
                Exit Select
            Case TransferProgressState.FileTransferring
                statusLabel.Text = "Processing file..."
                Exit Select
            Case TransferProgressState.DataBlockProcessed
                statusLabel.Text = "Transferring file..."
                Exit Select
            Case TransferProgressState.FileTransferred
                statusLabel.Text = "File transferred."
                Exit Select
        End Select

        ' process any application events to prevent the window from freezing
        Application.DoEvents()
    End Sub

    ''' <summary>
    ''' Handles the multi-file transfer problem detected event.
    ''' </summary>
    Private Sub ftp_ProblemDetected(sender As Object, e As FtpProblemDetectedEventArgs)
        _problemForm.ShowModal(Me, e)
    End Sub

End Class
