'  
'   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.Data
Imports System.Threading
Imports System.IO
Imports Rebex.Net

Public Class MainForm
    Inherits System.Windows.Forms.Form
    Private lblRemote As System.Windows.Forms.Label
    Private lblLocal As System.Windows.Forms.Label
    Private txtRemote As System.Windows.Forms.TextBox
    Private WithEvents cmdDownload As System.Windows.Forms.Button
    Private WithEvents cmdUpload As System.Windows.Forms.Button
    Private pbProgress As System.Windows.Forms.ProgressBar
    Private statusStrip As System.Windows.Forms.StatusStrip
    Private statusLabel As System.Windows.Forms.ToolStripStatusLabel
    Private WithEvents cmdAbort As System.Windows.Forms.Button
    Private txtLocal As System.Windows.Forms.TextBox
    Private txtPort As System.Windows.Forms.TextBox
    Private txtHostname As System.Windows.Forms.TextBox
    Private txtUsername As System.Windows.Forms.TextBox
    Private txtPassword As System.Windows.Forms.TextBox
    Private lblHostname As System.Windows.Forms.Label
    Private lblUsername As System.Windows.Forms.Label
    Private lblPort As System.Windows.Forms.Label
    Private lblPassword As System.Windows.Forms.Label

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

    ' Instance of Rebex.Net.Ftp
    Private _ftp As Ftp

    ' Remote offset to start at
    Private _remoteOffset As Long

    'Connection info
    Dim localPath As String
    Dim remotePath As String
    Dim hostname As String
    Dim username As String
    Dim password As String
    Dim port As Integer
    Dim secure As Boolean
    Dim acceptAll As Boolean

    ' This is called at the change of FTP object state
    Private Sub StateChanged(ByVal sender As Object, ByVal e As FtpStateChangedEventArgs)
        statusLabel.Text = e.NewState.ToString()
    End Sub 'StateChanged

    ' This is called on transfer progress
    Private Sub TransferProgress(ByVal sender As Object, ByVal e As FtpTransferProgressChangedEventArgs)
        pbProgress.Value = Convert.ToInt32(e.CurrentFileProgressPercentage)
        statusLabel.Text = "Transferred " & e.CurrentFileBytesTransferred & " bytes"
    End Sub 'TransferProgress

    Public Sub New()
        MyBase.New()

        CheckForIllegalCrossThreadCalls = True
        Application.EnableVisualStyles()

        InitializeComponent()

        OperationDone()
    End Sub    'New


    '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

#Region "Windows Form Designer generated code"

    '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 cbSecure As System.Windows.Forms.CheckBox
    Private WithEvents cbAcceptAll As System.Windows.Forms.CheckBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.txtLocal = New System.Windows.Forms.TextBox()
        Me.lblRemote = New System.Windows.Forms.Label()
        Me.lblLocal = New System.Windows.Forms.Label()
        Me.txtRemote = New System.Windows.Forms.TextBox()
        Me.cmdDownload = New System.Windows.Forms.Button()
        Me.cmdUpload = New System.Windows.Forms.Button()
        Me.pbProgress = New System.Windows.Forms.ProgressBar()
        Me.statusStrip = New System.Windows.Forms.StatusStrip()
        Me.statusLabel = New System.Windows.Forms.ToolStripStatusLabel()
        Me.cmdAbort = New System.Windows.Forms.Button()
        Me.txtPort = New System.Windows.Forms.TextBox()
        Me.txtHostname = New System.Windows.Forms.TextBox()
        Me.txtUsername = New System.Windows.Forms.TextBox()
        Me.txtPassword = New System.Windows.Forms.TextBox()
        Me.lblHostname = New System.Windows.Forms.Label()
        Me.lblUsername = New System.Windows.Forms.Label()
        Me.lblPort = New System.Windows.Forms.Label()
        Me.lblPassword = New System.Windows.Forms.Label()
        Me.cbSecure = New System.Windows.Forms.CheckBox()
        Me.cbAcceptAll = New System.Windows.Forms.CheckBox()
        Me.statusStrip.SuspendLayout()
        Me.SuspendLayout()
        '
        'txtLocal
        '
        Me.txtLocal.Location = New System.Drawing.Point(80, 56)
        Me.txtLocal.Name = "txtLocal"
        Me.txtLocal.Size = New System.Drawing.Size(248, 20)
        Me.txtLocal.TabIndex = 0
        Me.txtLocal.Text = "c:\temp\readme.txt"
        '
        'lblRemote
        '
        Me.lblRemote.Location = New System.Drawing.Point(8, 80)
        Me.lblRemote.Name = "lblRemote"
        Me.lblRemote.Size = New System.Drawing.Size(72, 24)
        Me.lblRemote.TabIndex = 1
        Me.lblRemote.Text = "Remote file:"
        Me.lblRemote.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblLocal
        '
        Me.lblLocal.Location = New System.Drawing.Point(8, 56)
        Me.lblLocal.Name = "lblLocal"
        Me.lblLocal.Size = New System.Drawing.Size(72, 24)
        Me.lblLocal.TabIndex = 2
        Me.lblLocal.Text = "Local file:"
        Me.lblLocal.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'txtRemote
        '
        Me.txtRemote.Location = New System.Drawing.Point(80, 80)
        Me.txtRemote.Name = "txtRemote"
        Me.txtRemote.Size = New System.Drawing.Size(248, 20)
        Me.txtRemote.TabIndex = 1
        Me.txtRemote.Text = "/pub/example/readme.txt"
        '
        'cmdDownload
        '
        Me.cmdDownload.Location = New System.Drawing.Point(192, 144)
        Me.cmdDownload.Name = "cmdDownload"
        Me.cmdDownload.Size = New System.Drawing.Size(64, 20)
        Me.cmdDownload.TabIndex = 9
        Me.cmdDownload.Text = "Download"
        '
        'cmdUpload
        '
        Me.cmdUpload.Location = New System.Drawing.Point(120, 144)
        Me.cmdUpload.Name = "cmdUpload"
        Me.cmdUpload.Size = New System.Drawing.Size(64, 20)
        Me.cmdUpload.TabIndex = 8
        Me.cmdUpload.Text = "Upload"
        '
        'pbProgress
        '
        Me.pbProgress.Location = New System.Drawing.Point(8, 144)
        Me.pbProgress.Name = "pbProgress"
        Me.pbProgress.Size = New System.Drawing.Size(104, 20)
        Me.pbProgress.TabIndex = 8
        '
        'statusStrip
        '
        Me.statusStrip.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.statusLabel})
        Me.statusStrip.Location = New System.Drawing.Point(0, 166)
        Me.statusStrip.Name = "statusStrip"
        Me.statusStrip.Size = New System.Drawing.Size(334, 22)
        Me.statusStrip.TabIndex = 9
        '
        'statusLabel
        '
        Me.statusLabel.Name = "statusLabel"
        Me.statusLabel.Size = New System.Drawing.Size(90, 21)
        Me.statusLabel.Text = "Ready"
        '
        'cmdAbort
        '
        Me.cmdAbort.Location = New System.Drawing.Point(264, 144)
        Me.cmdAbort.Name = "cmdAbort"
        Me.cmdAbort.Size = New System.Drawing.Size(64, 20)
        Me.cmdAbort.TabIndex = 10
        Me.cmdAbort.Text = "Abort"
        '
        'txtPort
        '
        Me.txtPort.Location = New System.Drawing.Point(272, 8)
        Me.txtPort.Name = "txtPort"
        Me.txtPort.Size = New System.Drawing.Size(56, 20)
        Me.txtPort.TabIndex = 3
        Me.txtPort.Text = "21"
        '
        'txtHostname
        '
        Me.txtHostname.Location = New System.Drawing.Point(80, 8)
        Me.txtHostname.Name = "txtHostname"
        Me.txtHostname.Size = New System.Drawing.Size(152, 20)
        Me.txtHostname.TabIndex = 2
        Me.txtHostname.Text = "test.rebex.net"
        '
        'txtUsername
        '
        Me.txtUsername.Location = New System.Drawing.Point(80, 32)
        Me.txtUsername.Name = "txtUsername"
        Me.txtUsername.Size = New System.Drawing.Size(88, 20)
        Me.txtUsername.TabIndex = 6
        Me.txtUsername.Text = "demo"
        '
        'txtPassword
        '
        Me.txtPassword.Location = New System.Drawing.Point(240, 32)
        Me.txtPassword.Name = "txtPassword"
        Me.txtPassword.PasswordChar = Global.Microsoft.VisualBasic.ChrW(42)
        Me.txtPassword.Size = New System.Drawing.Size(88, 20)
        Me.txtPassword.TabIndex = 7
        Me.txtPassword.Text = "password"
        '
        'lblHostname
        '
        Me.lblHostname.Location = New System.Drawing.Point(8, 8)
        Me.lblHostname.Name = "lblHostname"
        Me.lblHostname.Size = New System.Drawing.Size(72, 24)
        Me.lblHostname.TabIndex = 15
        Me.lblHostname.Text = "Hostname:"
        Me.lblHostname.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblUsername
        '
        Me.lblUsername.Location = New System.Drawing.Point(8, 32)
        Me.lblUsername.Name = "lblUsername"
        Me.lblUsername.Size = New System.Drawing.Size(72, 24)
        Me.lblUsername.TabIndex = 16
        Me.lblUsername.Text = "Username:"
        Me.lblUsername.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblPort
        '
        Me.lblPort.Location = New System.Drawing.Point(240, 8)
        Me.lblPort.Name = "lblPort"
        Me.lblPort.Size = New System.Drawing.Size(32, 24)
        Me.lblPort.TabIndex = 17
        Me.lblPort.Text = "Port:"
        Me.lblPort.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblPassword
        '
        Me.lblPassword.Location = New System.Drawing.Point(176, 32)
        Me.lblPassword.Name = "lblPassword"
        Me.lblPassword.Size = New System.Drawing.Size(64, 24)
        Me.lblPassword.TabIndex = 18
        Me.lblPassword.Text = "Password:"
        Me.lblPassword.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'cbSecure
        '
        Me.cbSecure.Location = New System.Drawing.Point(8, 112)
        Me.cbSecure.Name = "cbSecure"
        Me.cbSecure.Size = New System.Drawing.Size(88, 24)
        Me.cbSecure.TabIndex = 19
        Me.cbSecure.Text = "TLS/SSL"
        '
        'cbAcceptAll
        '
        Me.cbAcceptAll.Location = New System.Drawing.Point(80, 108)
        Me.cbAcceptAll.Name = "cbAcceptAll"
        Me.cbAcceptAll.Size = New System.Drawing.Size(248, 32)
        Me.cbAcceptAll.TabIndex = 20
        Me.cbAcceptAll.Text = "Do not validate any server certificates - this is insecure but useful for testing."
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(334, 188)
        Me.Controls.Add(Me.cbAcceptAll)
        Me.Controls.Add(Me.cbSecure)
        Me.Controls.Add(Me.lblPassword)
        Me.Controls.Add(Me.lblPort)
        Me.Controls.Add(Me.lblUsername)
        Me.Controls.Add(Me.lblHostname)
        Me.Controls.Add(Me.txtPassword)
        Me.Controls.Add(Me.txtUsername)
        Me.Controls.Add(Me.txtHostname)
        Me.Controls.Add(Me.txtPort)
        Me.Controls.Add(Me.cmdAbort)
        Me.Controls.Add(Me.statusStrip)
        Me.Controls.Add(Me.pbProgress)
        Me.Controls.Add(Me.cmdUpload)
        Me.Controls.Add(Me.cmdDownload)
        Me.Controls.Add(Me.txtRemote)
        Me.Controls.Add(Me.lblLocal)
        Me.Controls.Add(Me.lblRemote)
        Me.Controls.Add(Me.txtLocal)
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
        Me.MaximizeBox = False
        Me.MinimizeBox = False
        Me.Name = "MainForm"
        Me.Text = "Resumable FTP Transfer"
        Me.statusStrip.ResumeLayout(False)
        Me.statusStrip.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub 'InitializeComponent 
#End Region


    <STAThread()> _
    Shared Sub Main()
            ' set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key


        Application.Run(New MainForm)
    End Sub    'Main

    ' Download operation. This is run in a background thread.
    Private Async Sub Download()
        PrepareOperation()

        Dim local As Stream = Nothing
        Try
            If File.Exists(localPath) Then
                ' If the local file exists, ask whether to resume transfer.
                Dim res As DialogResult = ShowMessage("Local file already exists. Do you want to resume the transfer? (If you answer 'no', the file will be overwritten.)", "Question", MessageBoxButtons.YesNoCancel)
                Select Case res
                    Case System.Windows.Forms.DialogResult.Yes
                        ' Open for writing and seek to the end if we are resuming
                        local = File.OpenWrite(localPath)
                        local.Seek(0, SeekOrigin.End)
                    Case System.Windows.Forms.DialogResult.No
                        local = File.Create(localPath)
                    Case Else
                        Return
                End Select
            Else
                local = File.Create(localPath)
            End If

            Dim secureMode As SslMode = SslMode.None
            ' If TLS/SSL was checked, try to secure the connection.
            If secure Then
                _ftp.Settings.SslAcceptAllCertificates = acceptAll
                secureMode = SslMode.Explicit
            End If

            ' Connect to the server.
            Await _ftp.ConnectAsync(hostname, port, secureMode)

            ' Log in.
            Await _ftp.LoginAsync(username, password)

            ' Get the length of the remote file. This is needed to be able to resume the transfer.
            Try
                _remoteOffset = Await _ftp.GetFileLengthAsync(remotePath)
            Catch x As FtpException
                If x.Response.Code <> 550 Then
                    Throw
                End If
                _remoteOffset = -1
            End Try

            If _remoteOffset < 0 Then
                ShowMessage("Unable to download, remote file does not exist.", "Error")
                Return
            End If

            If local.Length > _remoteOffset Then
                ShowMessage("Unable to resume transfer, local file is longer than remote file.", "Error")
                Return
            End If

            ' Do the transfer if there data is available to download.
            If local.Length < _remoteOffset Then
                cmdAbort.Enabled = True
                _remoteOffset = local.Position
                Await _ftp.GetFileAsync(remotePath, local, _remoteOffset)
            End If

            ShowMessage("Download finished.", "Info")
        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Transfer aborted.", "Info")
            Else
                ShowMessage(ex.ToString(), "Error")
            End If
        Catch x As Exception
            ShowMessage(x.ToString(), "Error")
        Finally
            If Not (local Is Nothing) Then
                local.Close()
            End If

            Disconnect()
        End Try
    End Sub    'BackgroundDownload



    ' Download operation. Similar to upload operation.
    Private Async Sub Upload()
        PrepareOperation()

        Dim local As Stream = Nothing
        Try
            local = File.OpenRead(localPath)

            Dim secureMode As SslMode = SslMode.None
            ' If TLS/SSL was checked, try to secure the connection.
            If secure Then
                _ftp.Settings.SslAcceptAllCertificates = acceptAll
                secureMode = SslMode.Explicit
            End If

            ' Connect to the server.
            Await _ftp.ConnectAsync(hostname, port, secureMode)

            ' Log in.
            Await _ftp.LoginAsync(username, password)

            ' Get the length of the remote file. This is needed to be able to resume the transfer.
            Try
                _remoteOffset = Await _ftp.GetFileLengthAsync(remotePath)
            Catch x As FtpException
                If x.Response.Code <> 550 Then
                    Throw
                End If
                _remoteOffset = -1
            End Try

            If _remoteOffset >= 0 Then
                ' If the remote file exists, ask whether to resume transfer.
                Dim res As DialogResult = ShowMessage("Remote file already exists. Do you want to resume the transfer? (If you answer 'no', the file will be overwritten.)", "Question", MessageBoxButtons.YesNoCancel)
                Select Case res
                    Case System.Windows.Forms.DialogResult.Yes
                    Case System.Windows.Forms.DialogResult.No
                        _remoteOffset = 0
                    Case Else
                        Return
                End Select
            Else
                _remoteOffset = 0
            End If

            If _remoteOffset > local.Length Then
                ShowMessage("Unable to resume transfer, remote file is longer than local file.", "Error")
                Return
            End If

            ' Do the transfer if there data is available to upload.
            If _remoteOffset < local.Length Then
                cmdAbort.Enabled = True
                local.Seek(_remoteOffset, SeekOrigin.Begin)
                Await _ftp.PutFileAsync(local, remotePath, _remoteOffset, -1)
            End If

            ShowMessage("Upload finished.", "Info")
        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Transfer aborted.", "Info")
            Else
                ShowMessage(ex.ToString(), "Error")
            End If
        Catch x As Exception
            ShowMessage(x.ToString(), "Error")
        Finally
            If Not (local Is Nothing) Then
                local.Close()
            End If

            Disconnect()
        End Try
    End Sub    'BackgroundUpload

    Private Async Sub Disconnect()
        Await _ftp.DisconnectAsync()
        _ftp.Dispose()
        _ftp = Nothing

        OperationDone()
    End Sub

    Private Sub OperationDone()
        ' Reset the progress bar
        pbProgress.Value = 0

        ' Enable editable boxes and buttons
        txtHostname.Enabled = True
        txtPort.Enabled = True
        txtUsername.Enabled = True
        txtPassword.Enabled = True
        txtRemote.Enabled = True
        txtLocal.Enabled = True
        cmdDownload.Enabled = True
        cmdUpload.Enabled = True
        cbSecure.Enabled = True
        cbAcceptAll.Enabled = True

        cmdAbort.Enabled = False
    End Sub


    ' Starts the background transfer operation.
    Private Sub PrepareOperation()
        ' Initialize an instance of FTP class and set up events
        _ftp = New Ftp
        AddHandler _ftp.StateChanged, AddressOf Me.StateChanged
        AddHandler _ftp.TransferProgressChanged, AddressOf Me.TransferProgress

        ' Initialize connection info
        hostname = txtHostname.Text
        port = Convert.ToInt32(txtPort.Text)
        password = txtPassword.Text
        username = txtUsername.Text
        remotePath = txtRemote.Text
        localPath = txtLocal.Text
        secure = cbSecure.Checked
        acceptAll = cbAcceptAll.Checked

        ' Disable editable boxes and buttons
        txtHostname.Enabled = False
        txtPort.Enabled = False
        txtPassword.Enabled = False
        txtUsername.Enabled = False
        txtRemote.Enabled = False
        txtLocal.Enabled = False
        cmdDownload.Enabled = False
        cmdUpload.Enabled = False
        cbSecure.Enabled = False
        cbAcceptAll.Enabled = False
    End Sub    'StartBackgroundThread


    ' Abort the background thread on exit if it is active.
    Protected Overrides Sub OnFormClosed(ByVal e As FormClosedEventArgs)
        MyBase.OnFormClosed(e)

        Dim ftpTmp As Ftp = _ftp
        If (ftpTmp IsNot Nothing) Then
            Try
                If (ftpTmp.GetConnectionState().Connected) Then
                    ftpTmp.Abort()
                End If
            Catch x As ObjectDisposedException
                ' ftp object is already disposed, nothing to abort
            End Try
        End If
    End Sub    'OnFormClosed

    Private Sub cmdDownload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdDownload.Click
        Download()
    End Sub    'cmdDownload_Click


    Private Sub cmdUpload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdUpload.Click
        Upload()
    End Sub    'cmdUpload_Click


    ' Abort transfer if background operation is active.
    Private Sub cmdAbort_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdAbort.Click
        statusLabel.Text = "Aborting"

        _ftp.Abort()

        cmdAbort.Enabled = False
    End Sub    'cmdAbort_Click

    Private Function ShowMessage(text As String, title As String) As DialogResult
        Return ShowMessage(text, title, MessageBoxButtons.OK)
    End Function


    Private Function ShowMessage(text As String, title As String, buttons As MessageBoxButtons) As DialogResult
        ' on Mono, MessageBox.Show has to be called from the application's main thread
        If Me.InvokeRequired Then
            Return DirectCast(Me.Invoke(DirectCast(AddressOf ShowMessage, ShowMessageDelegate), text, title, buttons), DialogResult)
        End If

        Return MessageBox.Show(text, title, buttons)
    End Function

    Private Delegate Function ShowMessageDelegate(text As String, title As String, buttons As MessageBoxButtons) As DialogResult

End Class 'MainForm