'  
'   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.IO
Imports System.Net
Imports System.Windows.Forms
Imports Rebex.TerminalEmulation
Imports Rebex.Net


''' <summary>
''' The main form of the application. It contains the terminal control.
''' </summary>
Public Class MainForm
    Inherits Form

    ''' <summary>
    ''' The Ssh object. Used to communicate with SSH servers.
    ''' </summary>
    Private _ssh As Ssh

    ''' <summary>
    ''' The Telnet object. Used to comunicate with telnet servers.
    ''' </summary>
    Private _telnet As Telnet

    ''' <summary>
    ''' The Serial port. Used to communicate with server over RS-232.
    ''' </summary>
    Private _serialPort As SerialPortChannel

    ''' <summary>
    ''' Required designer variable.
    ''' </summary>
    Private components As System.ComponentModel.IContainer = Nothing
    Private terminalFontDialog As System.Windows.Forms.FontDialog
    Private saveDialog As System.Windows.Forms.SaveFileDialog
    Private menuItemFile As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemConnect As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemSave As System.Windows.Forms.ToolStripMenuItem
    Private menuItemTools As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemFont As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemOptions As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemStartRecording As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemStopRecording As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemDisconnect As System.Windows.Forms.ToolStripMenuItem
    Private recorderSaveDialog As System.Windows.Forms.SaveFileDialog

    ''' <summary>
    ''' Initialize this class.
    ''' </summary>
    Public Sub New()
        InitializeComponent()

        CheckForIllegalCrossThreadCalls = True

        ' set the size of the form to the size equal to 80 columns, 25 rows
        ClientSize = tcConsole.GetControlSize(80, 25)

        ' register the RemoteResize event 
        AddHandler tcConsole.RemoteResize, AddressOf Console_RemoteResize

        ' register the Disconnected event 
        AddHandler tcConsole.Disconnected, AddressOf Console_Disconnected

        ' set the terminal options from the config file
        tcConsole.Options = Settings.Default.TerminalOptions
    End Sub

    ''' <summary>
    ''' Clean up any resources being used.
    ''' </summary>
    ''' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso Not (components Is Nothing) Then
            components.Dispose()
        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 WithEvents mmenu As System.Windows.Forms.MenuStrip
    Private WithEvents tcConsole As TerminalControl
    Private WithEvents menuItemExit As System.Windows.Forms.ToolStripMenuItem
    Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container()
        Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm))
        Me.saveDialog = New System.Windows.Forms.SaveFileDialog()
        Me.terminalFontDialog = New System.Windows.Forms.FontDialog()
        Me.mmenu = New System.Windows.Forms.MenuStrip
        Me.menuItemFile = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemConnect = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemDisconnect = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemSave = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemExit = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemTools = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemFont = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemOptions = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemStartRecording = New System.Windows.Forms.ToolStripMenuItem()
        Me.menuItemStopRecording = New System.Windows.Forms.ToolStripMenuItem()
        Me.recorderSaveDialog = New System.Windows.Forms.SaveFileDialog()
        Me.tcConsole = New Rebex.TerminalEmulation.TerminalControl()
        Me.mmenu.SuspendLayout()
        Me.SuspendLayout()
        '
        'saveDialog
        '
        Me.saveDialog.Filter = resources.GetString("saveDialog.Filter")
        Me.saveDialog.RestoreDirectory = True
        '
        'mmenu
        '
        Me.mmenu.Items.AddRange(New System.Windows.Forms.ToolStripMenuItem() {Me.menuItemFile, Me.menuItemTools})
        '
        'menuItemFile
        '
        Me.menuItemFile.MergeIndex = 0
        Me.menuItemFile.DropDownItems.AddRange(New System.Windows.Forms.ToolStripMenuItem() {Me.menuItemConnect, Me.menuItemDisconnect, Me.menuItemSave, Me.menuItemExit})
        Me.menuItemFile.Text = "&File"
        '
        'menuItemConnect
        '
        Me.menuItemConnect.MergeIndex = 0
        Me.menuItemConnect.Text = "&Connect..."
        '
        'menuItemDisconnect
        '
        Me.menuItemDisconnect.Enabled = False
        Me.menuItemDisconnect.MergeIndex = 1
        Me.menuItemDisconnect.Text = "&Disconnect"
        '
        'menuItemSave
        '
        Me.menuItemSave.MergeIndex = 2
        Me.menuItemSave.Text = "&Save..."
        '
        'menuItemExit
        '
        Me.menuItemExit.MergeIndex = 3
        Me.menuItemExit.Text = "E&xit"
        '
        'menuItemTools
        '
        Me.menuItemTools.MergeIndex = 1
        Me.menuItemTools.DropDownItems.AddRange(New System.Windows.Forms.ToolStripMenuItem() {Me.menuItemFont, Me.menuItemOptions, Me.menuItemStartRecording, Me.menuItemStopRecording})
        Me.menuItemTools.Text = "&Tools"
        '
        'menuItemFont
        '
        Me.menuItemFont.MergeIndex = 0
        Me.menuItemFont.Text = "&Font..."
        '
        'menuItemOptions
        '
        Me.menuItemOptions.MergeIndex = 1
        Me.menuItemOptions.Text = "&Options..."
        '
        'menuItemStartRecording
        '
        Me.menuItemStartRecording.MergeIndex = 2
        Me.menuItemStartRecording.Text = "&Start recording..."
        '
        'menuItemStopRecording
        '
        Me.menuItemStopRecording.Enabled = False
        Me.menuItemStopRecording.MergeIndex = 3
        Me.menuItemStopRecording.Text = "S&top recording"
        '
        'recorderSaveDialog
        '
        Me.recorderSaveDialog.DefaultExt = "ans"
        Me.recorderSaveDialog.Filter = "Ansi file (*.ans;*.ansi)|*.ans;*.ansi|All files (*.*)|*.*"
        Me.recorderSaveDialog.OverwritePrompt = False
        '
        'tcConsole
        '
        Me.tcConsole.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.tcConsole.BackColor = System.Drawing.Color.Magenta
        Me.tcConsole.Cursor = System.Windows.Forms.Cursors.IBeam
        Me.tcConsole.CursorMouse = System.Windows.Forms.Cursors.Arrow
        Me.tcConsole.CursorText = System.Windows.Forms.Cursors.IBeam
        Me.tcConsole.HistoryMaxLength = 256
        Me.tcConsole.Location = New System.Drawing.Point(0, 27)
        Me.tcConsole.Name = "tcConsole"
        Me.tcConsole.Recorder = Nothing
        Me.tcConsole.Size = New System.Drawing.Size(737, 408)
        Me.tcConsole.TabIndex = 2
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(736, 435)
        Me.Controls.Add(Me.tcConsole)
        Me.Controls.Add(Me.mmenu)
        Me.MainMenuStrip = Me.mmenu
        Me.Name = "MainForm"
        Me.Text = "Rebex SSH and Telnet Terminal Client"
        Me.mmenu.ResumeLayout(False)
        Me.mmenu.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub

#End Region

    ''' <summary>
    ''' Handles the console Disconnected event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub Console_Disconnected(ByVal sender As Object, ByVal e As EventArgs)
        tcConsole.Screen.WriteLine()
        tcConsole.Screen.WriteLine("Session closed.")
        menuItemDisconnect.Enabled = False
    End Sub

    ''' <summary>
    ''' Handles the console RemoteResize event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub Console_RemoteResize(ByVal sender As Object, ByVal e As RemoteResizeEventArgs)
        ' set the size of the form to the size of the console
        ClientSize = tcConsole.GetControlSize(e.Columns, e.Rows)

        ' console screen is not set immediately, set it manually right now
        tcConsole.SetScreenSize(e.Columns, e.Rows)
    End Sub

    ''' <summary>
    ''' Handles the 'Font' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemFont_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemFont.Click
        ' show dialog
        If terminalFontDialog.ShowDialog() <> DialogResult.OK Then
            Return
        End If

        ' check whether the selected font family is supported
        If Not TerminalFont.IsSupportedFamily(terminalFontDialog.Font.FontFamily) Then
            MessageBox.Show(String.Format("Font {0} is not supported. Please choose another.", terminalFontDialog.Font.FontFamily.Name))
            Return
        End If

        ' set selected font to the console
        tcConsole.TerminalFont = New TerminalFont(terminalFontDialog.Font.FontFamily, terminalFontDialog.Font.Size)
    End Sub

    ''' <summary>
    ''' Handles the 'Connect' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemConnect.Click
        Try
            ' connection dialog
            Dim con As New Connection

            ' show connection dialog
            con.ShowDialog()

            ' if connection dialog was not confirmed, return
            If Not con.OK Then
                Return
            End If

            ' unbind the client object from the terminal console
            UnbindClient()

            ' clear the screen
            tcConsole.Screen.Clear()

            ' if proxy was enabled, set up the proxy property
            Dim proxy As Proxy = Nothing
            If con.ProxyEnabled Then
                Dim proxyNetworkCredential As NetworkCredential = Nothing

                If con.ProxyLoginName.Length > 0 Then
                    If con.ProxyPassword.Length = 0 Then
                        con.ProxyPassword = Nothing
                    End If

                    proxyNetworkCredential = New System.Net.NetworkCredential(con.ProxyLoginName, con.ProxyPassword)
                End If

                proxy = New Proxy(con.ProxyType, con.ProxyHostName, con.ProxyPort)
                proxy.AuthenticationMethod = ProxyAuthentication.Basic
                proxy.Credentials = proxyNetworkCredential
            End If

            Select Case con.Protocol
                Case Connection.ProtocolType.Ssh
                    ' create a new Ssh object
                    _ssh = New Ssh

                    ' assign proxy if enabled
                    If Not proxy Is Nothing Then _ssh.Proxy = proxy

                    ' connect to the SSH server
                    _ssh.Connect(con.HostName, con.Port)

                    Dim privateKey As SshPrivateKey = Nothing

                    ' if private key was specified, show the passphrase dialog and set up the SshPrivateKey object
                    If con.PrivateKey.Length > 0 Then
                        Dim pp As New PassphraseDialog
                        If pp.ShowDialog() = DialogResult.OK Then
                            privateKey = New SshPrivateKey(con.PrivateKey, pp.Passphrase)
                        Else
                            Throw New Exception("Password not entered.")
                        End If
                    End If

                    If con.Password.Length = 0 Then
                        con.Password = Nothing
                    End If

                    ' login to the SSH server
                    _ssh.Login(con.LoginName, con.Password, privateKey)

                    ' bind the connected and authenticated Ssh instance to the terminal console
                    tcConsole.Bind(_ssh)
                    Exit Select
                Case Connection.ProtocolType.Telnet
                    ' create a new Telnet object
                    _telnet = New Telnet(con.HostName, con.Port)

                    ' assign proxy if enabled
                    If Not proxy Is Nothing Then _telnet.Proxy = proxy

                    ' bind the Telnet instance to the terminal console
                    ' terminal connects to the server automatically at a moment
                    tcConsole.Bind(_telnet)
                    Exit Select
                Case Connection.ProtocolType.SerialPort
                    ' create a new Serial port object
                    _serialPort = New SerialPortChannel(con.PortName, con.BaudRate)

                    ' bind the Serial port instance to the terminal console
                    ' (connects to the server automatically)
                    tcConsole.Bind(_serialPort)
                    Exit Select
            End Select

            menuItemDisconnect.Enabled = True
        Catch ex As Exception
            MessageBox.Show(String.Format("Exception occured: {0}", ex))
        End Try
    End Sub

    ''' <summary>
    ''' Handles the 'Disconnect' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemDisconnect_Click(sender As Object, e As EventArgs) Handles menuItemDisconnect.Click
        ' unbind the client object
        UnbindClient()
    End Sub

    ''' <summary>
    ''' Handles the 'Option' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemOptions_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemOptions.Click
        ' show options dialog
        Dim options As New Options
        If options.ShowDialog() = DialogResult.OK Then
            tcConsole.Options = options.TerminalOptions
        End If
    End Sub

    ''' <summary>
    ''' Handles the 'Save As' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemSave.Click
        ' show Save As dialog
        If saveDialog.ShowDialog() <> DialogResult.OK Then
            Return
        End If

        ' get selected extension
        Dim extensionsArray As String() = saveDialog.Filter.Split("|"c)
        Dim extensions As String = extensionsArray(2 * saveDialog.FilterIndex - 1)
        Dim extensionArray As String() = extensions.Split(";"c)
        Dim extension As String = extensionArray(0)

        ' parse TerminalCaptureFormat from the selected extension
        Dim captureFormat As TerminalCaptureFormat
        Select Case extension.ToLower()
            Case "*.bmp"
                captureFormat = TerminalCaptureFormat.Bmp
                Exit Select
            Case "*.gif"
                captureFormat = TerminalCaptureFormat.Gif
                Exit Select
            Case "*.html", "*.htm"
                captureFormat = TerminalCaptureFormat.Html
                Exit Select
            Case "*.jpeg", "*.jpg"
                captureFormat = TerminalCaptureFormat.Jpeg
                Exit Select
            Case "*.png"
                captureFormat = TerminalCaptureFormat.Png
                Exit Select
            Case "*.tiff", "*.tif"
                captureFormat = TerminalCaptureFormat.Tiff
                Exit Select
            Case "*.ansi", "*.ans"
                captureFormat = TerminalCaptureFormat.Ansi
                Exit Select
            Case Else
                captureFormat = TerminalCaptureFormat.Text
                Exit Select
        End Select

        ' save the terminal console buffer
        tcConsole.Save(saveDialog.FileName, captureFormat, TerminalCaptureOptions.SaveTerminalResolution)
    End Sub

    ''' <summary>
    ''' Handles the 'Start recording' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemStartRecording_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemStartRecording.Click
        ' set the recorder file from config (it was set when options form was submited)
        Dim fileName As String = Settings.Default.GetString("RecorderFile")

        ' if recorder file is not specified, prompt the user to select one
        If fileName Is Nothing OrElse fileName.Length = 0 Then
            If recorderSaveDialog.ShowDialog() <> DialogResult.OK Then
                Return
            End If

            fileName = recorderSaveDialog.FileName
        End If

        Try
            ' try open the file for append and bind it to the terminal control
            tcConsole.Recorder = File.AppendText(fileName)
        Catch ex As Exception
            MessageBox.Show(String.Format("Exception occured: {0}", ex))
            Return
        End Try

        ' enable the 'Stop' menu item and disable the 'Start' menu item
        menuItemStartRecording.Enabled = False
        menuItemStopRecording.Enabled = True
    End Sub

    ''' <summary>
    ''' Handles the 'Stop Recording' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemStopRecording_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemStopRecording.Click
        ' unbind the recorder
        UnbindRecorder()

        ' enable the 'Start' menu item and disable the 'Stop' menu item
        menuItemStartRecording.Enabled = True
        menuItemStopRecording.Enabled = False
    End Sub

    ''' <summary>
    ''' Handles the 'Exit' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemExit.Click
        ' close the form
        Close()
    End Sub

    ''' <summary>
    ''' Handles the form closing event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MainForm_Closing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        ' unbind the client object
        UnbindClient()

        ' unbind the recorder
        UnbindRecorder()
    End Sub

    ''' <summary>
    ''' Unbinds the client object from the terminal control.
    ''' </summary>
    Private Sub UnbindClient()
        If Not _ssh Is Nothing Or Not _telnet Is Nothing Or Not _serialPort Is Nothing Then
            ' unbind the client object from the terminal console
            tcConsole.Unbind()

            If Not _ssh Is Nothing Then
                ' disconnect & dispose the client object
                _ssh.Disconnect()
                _ssh.Dispose()
            End If

            _ssh = Nothing
            _telnet = Nothing
            _serialPort = Nothing
        End If
    End Sub

    ''' <summary>
    ''' Unbinds the recorder from the terminal control.
    ''' </summary>
    Private Sub UnbindRecorder()
        ' unbind and close the recorder if it was bound
        If Not tcConsole.Recorder Is Nothing Then
            Dim writer As TextWriter = tcConsole.Recorder
            tcConsole.Recorder = Nothing
            writer.Close()
        End If
    End Sub

End Class

