'  
'   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.Collections.Generic
Imports System.Drawing
Imports System.IO
Imports System.Net
Imports System.Windows.Forms
Imports System.Text

Imports Rebex
Imports Rebex.Net
Imports Rebex.Net.Servers
Imports Rebex.Samples

''' <summary>
''' Main form. Starts the server and shows its UI.
''' </summary>
Partial Public Class MainForm
    Inherits Form
    ' sample user credentials
    Private _username As String = "user01"
    Private _password As String = "password"

    ' sample user
    Private _user01 As FileServerUser

    ' sample user's virtual root
    Private _userVirtualRoot As String = "."

    ' server port and 
    Private _port As Integer = 8022
    Private _server As FileServer

    ' RichTextBox-based ILogWriter
    Private _serverLog As RichTextBoxLogWriter

    ' server state
    Private _running As Boolean
    Private _firstTimeStart As Boolean = True

    Private Const MessageBoxTitle As String = "Simple File Server (SFTP, SCP, SSH)."

    ''' <summary>
    ''' The MainForm constructor.
    ''' </summary>
    Public Sub New()
        InitializeComponent()
        CheckForIllegalCrossThreadCalls = True

        folderBrowserDialog.Description = String.Format("Select the directory that you want to use as the virtual root for '{0}'.", _username)
        _userVirtualRoot = Path.GetFullPath(_userVirtualRoot)
        Me.tbVirtualRoot.Text = _userVirtualRoot

        ' create RichTextBox-based log writer
        _serverLog = New RichTextBoxLogWriter(tbLog, tbLog.MaxLength, LogLevel.Debug)
        ' LogLevel controls the log verbosity

        ' set up the server
        Try
            _server = New FileServer()
            _server.LogWriter = _serverLog

            ' add private key
            Dim rsaKey As SshPrivateKey = ServerKey.GetServerPrivateKey()
            _server.Keys.Add(rsaKey)

            ' add a sample user
            _user01 = New FileServerUser(_username, _password, _userVirtualRoot)
            _server.Users.Add(_user01)

            DisplayWelcome()
            btnChangeVirtualRoot.Enabled = True
            btnStartStop.Enabled = True
            btnBrowseRoot.Enabled = True
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    Private Sub BindToPort(port As Integer)
        ' allow SFTP subsystem
        _server.Bind(port, FileServerProtocol.Sftp)

        ' allow shell-like SCP subsystem
        _server.Bind(port, FileServerProtocol.Scp)
    End Sub

    Private Function GetPort() As Integer
        Dim result As Integer
        If Not Integer.TryParse(tbPort.Text, result) Then
            Throw New FormatException("The port is not a valid port number.")
        End If

        Return result
    End Function

    ''' <summary>
    ''' Start the server listeners.
    ''' </summary>
    Private Sub StartServer()
        If _firstTimeStart Then
            _port = GetPort()
            BindToPort(_port)
            _firstTimeStart = False
        Else
            ' check whether the port has changed
            Dim newPort As Integer = GetPort()
            If _port <> newPort Then
                ' unbind old port
                _server.Unbind(_port)

                ' bind to new port
                BindToPort(newPort)
                _port = newPort
            End If
        End If

        _server.Start()
        _running = True
        Me.btnStartStop.Text = "&Stop"
        Me.lblServerInfo.Text = String.Format("Running. Listening at port {0}.", _port)

        DisplayConnectionInfo()
    End Sub

    ''' <summary>
    ''' Stops listening for new connections to the server. (Active connections will be maintained).
    ''' </summary>
    Private Sub StopServer()
        _server.[Stop]()
        _running = False
        Me.btnStartStop.Text = "&Start"
        Me.lblServerInfo.Text = "Server has been stopped."
    End Sub

    ''' <summary>
    ''' Handles the Start/Stop button click.
    ''' </summary>
    Private Sub btnStartStop_Click(sender As Object, e As EventArgs) Handles btnStartStop.Click
        Try
            If Not _running Then
                StartServer()
            Else
                StopServer()
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    ''' <summary>
    ''' Stops the server when the form is closed.
    ''' </summary>
    Private Sub MainForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        If _running Then
            StopServer()
        End If
    End Sub

    ''' <summary>
    ''' Handles the virtual root change for the sample user.
    ''' </summary>
    ''' <param name="newPath">The path selected by user.</param>
    Private Sub ChangeVirtualRoot(newPath As String)
        If Not Directory.Exists(newPath) Then
            MessageBox.Show(String.Format("The path could not be found.", newPath), MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.[Error])
            tbVirtualRoot.Text = _userVirtualRoot
            Return
        End If

        newPath = Path.GetFullPath(newPath)

        If newPath <> _userVirtualRoot Then
            ' update paths
            tbVirtualRoot.Text = newPath
            _userVirtualRoot = newPath

            ' remove the sample user with the old virtual root path
            _server.Users.Remove(_user01)

            ' add a new sample user with the new virtual root path
            _user01 = New FileServerUser(_username, _password, _userVirtualRoot)
            _server.Users.Add(_user01)


            If _running Then
                MessageBox.Show("The change will apply to new sessions.", MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information)
            Else
                MessageBox.Show("The virtual root has been changed.", MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information)
            End If
        Else
            MessageBox.Show(String.Format("The path is the current virtual directory. No change will be performed.", newPath), MessageBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
    End Sub

    ''' <summary>
    ''' Handles the Browse button click.
    ''' </summary>
    Private Sub btnBrowseRoot_Click(sender As Object, e As EventArgs) Handles btnBrowseRoot.Click
        Dim result As DialogResult = folderBrowserDialog.ShowDialog()
        If result = DialogResult.OK Then
            ChangeVirtualRoot(folderBrowserDialog.SelectedPath)
        End If
    End Sub

    ''' <summary>
    ''' Handles the Change button click.
    ''' </summary>
    Private Sub btnChangeVirtualRoot_Click(sender As Object, e As EventArgs) Handles btnChangeVirtualRoot.Click
        ChangeVirtualRoot(tbVirtualRoot.Text)
    End Sub

    ''' <summary>
    ''' Writes an exception to the log.
    ''' </summary>
    ''' <param name="x">The exception.</param>
    Public Sub WriteToLog(x As Exception)
        _serverLog.WriteToLog("* " & x.ToString(), RichTextBoxLogWriter.COLORERROR)
    End Sub

    ''' <summary>
    ''' Displays welcome message.
    ''' </summary>
    Private Sub DisplayWelcome()
        _serverLog.WriteToLog("Welcome to Rebex File Server! To connect to the server, use an SFTP, SCP or SSH client." & vbLf, Color.DarkGreen)
    End Sub

    ''' <summary>
    ''' Displays information on how to connect to the server (IP address, port, username, password).
    ''' </summary>
    Private Sub DisplayConnectionInfo()
        ' use DNS to get the host's IP addresses
        Dim host As String = Dns.GetHostName()
        Dim ip As IPHostEntry = Dns.GetHostEntry(host)
        Dim ipAddresses As New StringBuilder()
        For Each ipAddress As IPAddress In ip.AddressList
            If ipAddress.AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork Then
                ipAddresses.AppendFormat("  {0}:{1}" & vbLf, ipAddress, _port)
            End If
        Next

        ' display server IP addresses, port, username and password
        _serverLog.WriteToLog(Color.DarkBlue, vbLf & "Accepting connections at:" & vbLf & "{0}" & vbLf & "Username: {1}" & vbLf & "Password: {2}" & vbLf, ipAddresses, _username, _password)
    End Sub

End Class
