Imports DigitalData.Modules.Logging
Imports Limilabs.Mail
Imports Limilabs.Client.IMAP
Imports Limilabs.Client.SMTP
Imports Limilabs.Client
Imports System.Net.Security
Imports System
Imports System.Security.Authentication
Imports Limilabs.Mail.Headers
Imports Limilabs.Mail.MIME
Imports Limilabs

Public Class Limilab
    Private Initialized As Boolean = False
    Private LogConfig As LogConfig
    Private Logger As DigitalData.Modules.Logging.Logger
    Private IMAPServer As String
    Private IMAPPort As Integer
    Private User As String
    Private Password As String
    Private AuthType As String
    Public CurrentImapObject As Imap
    Public ErrorMessage As String
    Private CURR_ListUIDs As List(Of Long)
    Public Sub New(LogConfig As LogConfig)
        LogConfig = LogConfig
        Logger = LogConfig.GetLogger()
    End Sub
    ''' <summary>
    ''' Initializes the module.
    ''' </summary>
    ''' <param name="oImapServer">IP-Address or Domainname of Server</param>
    ''' <param name="oPort">IMAP-Port</param>
    ''' <param name="oUser">IMAP-Username</param>
    ''' <param name="oPassword">IMAP-Password</param>
    ''' <param name="oAuthType">Auth-Type</param>
    ''' <param name="Folder">The folder to fetch messages from. Defaults to `Inbox`</param>
    Public Sub InitIMAP(Log_enabled As Boolean, oImapServer As String, oPort As Integer, oUser As String, oPassword As String, oAuthType As String, Optional Folder As String = "Inbox")
        LOG_Limilab(Log_enabled)
        IMAPServer = oImapServer
        IMAPPort = oPort
        User = oUser
        Password = oPassword
        AuthType = oAuthType
        Initialized = True

    End Sub
    Public Function CloseImap() As Boolean
        Try
            If Initialized = False Then
                Return True
            Else
                If Not IsNothing(CurrentImapObject) Then
                    CurrentImapObject.Close()
                End If

                Return True
            End If

        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return False
        End Try


    End Function

    Private Sub LOG_Limilab(Log_enabled As Boolean)
        Log.Enabled = Log_enabled
    End Sub

    ''' <summary>
    ''' Tests connection to a given IMAP Server by connecting and doing a simple message query.
    ''' </summary>
    ''' <returns>True if connection and query were successful. False otherwise.</returns>
    Public Function IMAPTestLogin() As Boolean
        Logger.Debug("Starting IMAPTestLogin ...")
        If Initialized = False Then
            Return False
        End If
        Try
            Logger.Debug("Connecting...")
            Dim oReturn As Boolean = ImapConnect()
            If oReturn = True Then
                CurrentImapObject.Close()
            End If
            Return oReturn
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return False
        End Try
    End Function
    Public Function IMAPGetUnseenMessageIDs() As List(Of Long)
        Dim oListuids As New List(Of Long)
        Logger.Debug("Starting IMAPGetMessageIDs ...")
        If Initialized = False Then
            Return Nothing
        End If
        Try
            Dim oConnect As Boolean = ImapConnect()

            If oConnect = True Then
                oListuids = ImapGetMessageIDs_Unseen()
                CURR_ListUIDs = oListuids
            End If
            Return oListuids
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return Nothing
        End Try
    End Function
    Public Function IMAPGetMessageIDs_AllMails() As List(Of Long)
        Logger.Debug("Starting IMAPGetMessageIDs ...")
        If Initialized = False Then
            Return Nothing
        End If
        Try
            Dim oConnectionSuccessful As Boolean = ImapConnect()
            Dim oListuids As List(Of Long)

            If oConnectionSuccessful = True Then
                Logger.Debug("Checking for new messages..")
                oListuids = ImapGetMessageIDs_All()
                CURR_ListUIDs = oListuids
            Else
                Logger.Warn("Connection was NOT successful. Returning Nothing.")
                Return Nothing
            End If
            Return oListuids
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return Nothing
        End Try
    End Function
    Private Function ImapConnect() As Boolean
        Try
            If Initialized = False Then
                Return True
            End If
            Logger.Debug("ImapConnect {0}:{1} with user {2}", IMAPServer, IMAPPort, User)
            Dim oReturnImap As New Imap()
            AddHandler oReturnImap.ServerCertificateValidate, AddressOf Validate
            Logger.Debug($"AUTH_TYPE  [{AuthType}]")
            If AuthType = "SSL/TLS" Then
                If IMAPPort <> "993" Then
                    Logger.Debug($"Connecting with explizit port [{IMAPPort}]")
                    oReturnImap.Connect(IMAPServer, IMAPPort)
                Else
                    Logger.Debug("Connecting to IMAP-Server without port...")
                    oReturnImap.ConnectSSL(IMAPServer)
                End If
                Logger.Debug($"Connect to [{IMAPServer}] successful!")

                Dim oSupportsStartTLS As Boolean = oReturnImap.SupportedExtensions().Contains(ImapExtension.StartTLS)
                If oSupportsStartTLS And AuthType.EndsWith("TLS") Then
                    Logger.Debug("Server supports StartTLS, so starting...")
                    oReturnImap.StartTLS()
                Else
                    Logger.Info("Server supports no StartTLS")
                    oReturnImap.SSLConfiguration.EnabledSslProtocols = SslProtocols.Tls12
                End If
            ElseIf AuthType = "SSL" Then
                If IMAPPort <> "993" Then
                    Logger.Debug($"Connecting with explizit port [{IMAPPort}]")
                    oReturnImap.Connect(IMAPServer, IMAPPort)
                Else
                    Logger.Debug("Connecting to IMAP-Server without port...")
                    oReturnImap.ConnectSSL(IMAPServer)
                End If
            ElseIf AuthType = "Simple" Then

            End If
            Logger.Debug(">> Connected to IMAP-Server!")

            Logger.Debug("Login with User and password...")
            oReturnImap.UseBestLogin(User, Password)
            Logger.Debug(">> Logged on!")
            CurrentImapObject = oReturnImap
            Return True
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            If Not IsNothing(ex.InnerException) Then
                Logger.Warn("Inner Exception ImapConnect: " + ex.InnerException.Message)
            End If


            Return False
        End Try
    End Function

    Private Sub Validate(
       ByVal sender As Object,
       ByVal e As ServerCertificateValidateEventArgs)

        Const ignoredErrors As SslPolicyErrors =
           SslPolicyErrors.RemoteCertificateChainErrors Or _    ' self-signed
           SslPolicyErrors.RemoteCertificateNameMismatch        ' name mismatch

        Dim nameOnCertificate As String = e.Certificate.Subject

        If (e.SslPolicyErrors And Not ignoredErrors) = SslPolicyErrors.None Then
            e.IsValid = True
            Return
        End If
        e.IsValid = False
    End Sub
    Private Function ImapGetMessageIDs_Unseen() As List(Of Long)
        Dim oListuids As New List(Of Long)
        Try
            CurrentImapObject.SelectInbox()

            oListuids = CurrentImapObject.Search(Flag.Unseen)

            Return oListuids
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return Nothing
        End Try

    End Function
    Private Function ImapGetMessageIDs_All() As List(Of Long)
        Dim oListuids As New List(Of Long)
        Try
            Logger.Debug("Opening Inbox..")
            CurrentImapObject.SelectInbox()

            Logger.Debug("Searching messages..")
            oListuids = CurrentImapObject.Search(Flag.All)

            Logger.Debug("[{0}] messages found.", oListuids.Count)
            Return oListuids
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return Nothing
        End Try

    End Function
    ''' <summary>
    ''' Creates a MailObject and sends Mail via smtp.
    ''' </summary>
    ''' <returns>True if Message was send. False otherwise.</returns>
    Public Function NewSMTPEmail(mailto As String, mailSubject As String, mailBody As String,
                                                    mailfrom As String, mailsmtp As String, mailport As Integer, mailUser As String, mailPW As String,
                                                    AUTH_TYPE As String, SENDER_INSTANCE As String, ADDED_DATETIME As String, Optional attachmentString As String = "", Optional Test As Boolean = False)
        Try
            Dim oError As Boolean = False
            Dim oReceipiants As String()
            If mailto.Contains(";") Then
                oReceipiants = mailto.Split(";")
            Else
                ReDim Preserve oReceipiants(0)
                oReceipiants(0) = mailto
            End If
            For Each oMailReceipiant As String In oReceipiants
                Logger.Debug($"oMailReceipiant [{oMailReceipiant}]")
                Logger.Debug($"mailsmtp [{mailsmtp}]")
                Logger.Debug($"mailport [{mailport}]")
                Logger.Debug($"mailSubject [{mailSubject}]")

                Dim oMailBuilder As New MailBuilder()
                oMailBuilder.From.Add(New MailBox(mailfrom))
                oMailBuilder.[To].Add(New MailBox(oMailReceipiant))
                oMailBuilder.Subject = mailSubject
                'If ADDED_DATETIME <> "" Then
                '    mailBody &= "<p>Creation-time: " & ADDED_DATETIME
                'End If
                If Test = True Then
                    oMailBuilder.Html = $"This is a Testmail! <p> The body-text will be replaced within profile! <p> mailsmtp: {mailsmtp} <br> mailport: {mailport}
                                        <br> mailUser: {mailUser} <br> mailPW: XXXX <br> AUTH_TYPE: {AUTH_TYPE}"

                Else
                    oMailBuilder.Html = mailBody
                End If

                Logger.Debug($"mailBody [{oMailBuilder.Html.ToString}]")

                If attachmentString <> "" Then
                    ' Read attachment from disk, add it to Attachments collection
                    If System.IO.File.Exists(attachmentString) Then
                        Dim oAttachment As MimeData = oMailBuilder.AddAttachment(attachmentString)
                    End If
                End If

                Dim email As IMail = oMailBuilder.Create()
                ' Send the message
                Using oSmtp As New Limilabs.Client.SMTP.Smtp()
                    AddHandler oSmtp.ServerCertificateValidate, AddressOf Validate
                    Logger.Debug($"AUTH_TYPE  [{AUTH_TYPE}]")
                    If AUTH_TYPE = "SSL" Then

                        Try
                            If mailport <> "465" Then
                                Logger.Debug($"Connecting with explizit port  [{mailport}]")
                                oSmtp.Connect(mailsmtp, mailport)
                                Logger.Debug($"Connect to [{mailsmtp}] successful!")
                            Else
                                oSmtp.ConnectSSL(mailsmtp)
                            End If
                        Catch ex As Exception
                            Logger.Error(ex)
                        End Try
                    ElseIf AUTH_TYPE = "SSL/TLS" Then
                        '##########################################################################################
                        'Tested with ExchangeServer SWB 22.10.2021
                        '##########################################################################################
                        If mailport <> "587" Then
                            Logger.Debug($"Connecting with explizit port  [{mailport}]")
                            oSmtp.Connect(mailsmtp, mailport)
                        Else
                            oSmtp.Connect(mailsmtp)
                        End If
                        Logger.Debug($"Connect to [{mailsmtp}] successful!")
                        Dim supportsStartTLS As Boolean = oSmtp.SupportedExtensions().Contains(SmtpExtension.StartTLS)
                        If supportsStartTLS = True Then
                            oSmtp.StartTLS()
                            Logger.Debug($"TLS started!")
                        Else
                            Logger.Info("Server supports no StartTLS")
                            oSmtp.SSLConfiguration.EnabledSslProtocols = SslProtocols.Tls12
                        End If
                    Else
                        oSmtp.Connect(mailsmtp)
                    End If
                    Logger.Debug($"mailUser [{mailUser}]")
                    If mailUser <> String.Empty Then
                        oSmtp.UseBestLogin(mailUser, mailPW)   ' remove if not needed
                    End If


                    oSmtp.SendMessage(email)
                    Logger.Info("Message to " & oMailReceipiant & " has been send.")
                    oSmtp.Close()
                End Using
            Next
            Return True

        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return False
        End Try

    End Function
    Public Function GetMailInfo(UID As Long) As Boolean
        Try
            Dim eml = CurrentImapObject.GetMessageByUID(UID)
            Dim email As IMail = New MailBuilder().CreateFromEml(eml)
            ' Subject
            Console.WriteLine(email.Subject)

            ' From
            For Each m As MailBox In email.From
                Console.WriteLine(m.Address)
                Console.WriteLine(m.Name)
            Next
            ' Date
            Console.WriteLine(email.[Date])
            ' Text body of the message
            Console.WriteLine(email.Text)
            ' Html body of the message
            Console.WriteLine(email.Html)
            ' Custom header
            Console.WriteLine(email.Document.Root.Headers("x-spam-value"))
            ' Save all attachments to disk
            For Each mime As MimeData In email.Attachments
                mime.Save("c:\" + mime.SafeFileName)
            Next
            Return True
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return False
        End Try

    End Function
    Public Function GetMailObjects() As ArrayList
        Try
            Dim WORKMAIL_LIST As New ArrayList()
            For Each oUID In CURR_ListUIDs
                Dim oEml = CurrentImapObject.GetMessageByUID(oUID)
                Dim oEmail As IMail = New MailBuilder().CreateFromEml(oEml)
                WORKMAIL_LIST.Add(oEmail)
            Next
            Return WORKMAIL_LIST
        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return Nothing
        End Try

    End Function
    Public Function IMAP_DeleteByUID(UID As Long) As Boolean
        Try
            If Not IsNothing(CurrentImapObject) Then
                CurrentImapObject.DeleteMessageByUID(UID)
                Return True
            Else
                Return False
            End If

        Catch ex As Exception
            Logger.Error(ex)
            ErrorMessage = ex.Message
            Return False
        End Try
    End Function
End Class
