Messaging: WIP MailFetcher, MailSession

This commit is contained in:
Jonathan Jenne 2023-08-22 08:38:46 +02:00
parent f6046aec54
commit 77d8a0825f
8 changed files with 700 additions and 296 deletions

View File

@ -66,7 +66,7 @@ Public Class Limilab
End Function End Function
Private Function LOG_Limilab(Log_enabled As Boolean) As Boolean Private Function LOG_Limilab(Log_enabled As Boolean) As Boolean
Mail.Log.Enabled = Log_enabled Log.Enabled = Log_enabled
End Function End Function
''' <summary> ''' <summary>

View File

@ -0,0 +1,177 @@
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports Limilabs.Client.IMAP
Imports Limilabs.Client
Imports Limilabs.Mail
Namespace Mail
Public Class MailFetcher
Inherits BaseClass
Private ReadOnly MailSession As MailSession
Private ReadOnly MailBuilder As New MailBuilder
Public ReadOnly Property Connected2Server As Boolean
Get
Return MailSession.Session.Connected
End Get
End Property
Public ReadOnly Property Client As Imap
Get
Return MailSession?.Client
End Get
End Property
Public Sub New(pLogConfig As LogConfig)
MyBase.New(pLogConfig)
MailSession = New MailSession(pLogConfig, New Imap)
Log.Enabled = pLogConfig.Debug
End Sub
Public Function Connect(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String) As MailSession.SessionInfo
Return MailSession.ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType, New MailSession.MailSessionOptions)
End Function
Public Function Connect(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSession.MailSessionOptions) As MailSession.SessionInfo
Return MailSession.ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType, pOptions)
End Function
Public Function Disconnect() As Boolean
Return MailSession.DisconnectFromServer()
End Function
Public Async Function FetchAllMails() As Task(Of List(Of IMail))
Return Await FetchMailsAsync(Flag.All)
End Function
Public Async Function FetchUnreadMails() As Task(Of List(Of IMail))
Return Await FetchMailsAsync(Flag.Unseen)
End Function
Public Function ListAllMails() As List(Of Long)
Return ListMails(Flag.All)
End Function
Public Function ListUnseenMails() As List(Of Long)
Return ListMails(Flag.Unseen)
End Function
Public Async Function FetchMailsAsync(pMailFlag As Flag) As Task(Of List(Of IMail))
Dim oMailIds = ListMails(pMailFlag)
Dim oMails As New List(Of IMail)
For Each oId In oMailIds
Dim oMail As IMail = Await FetchMailAsync(oId)
oMails.Add(oMail)
Next
Return oMails
End Function
Public Function ListMails(pMailFlag As Flag) As List(Of Long)
Try
If MailSession.Session.Connected Then
Dim oClient As Imap = MailSession.Client
Dim oStatus = oClient.SelectInbox()
Dim oMailIds = oClient.Search(pMailFlag)
Logger.Debug("Fetched [{0}] mail objects.", oMailIds.Count)
Return oMailIds
Else
Logger.Error("No connection to Server. Exiting.")
Return Nothing
End If
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Public Async Function ListMailsAsync(pMailFlag As Flag) As Task(Of List(Of Long))
Try
If MailSession.Session.Connected Then
Dim oClient As Imap = MailSession.Client
Dim oStatus = Await oClient.SelectInboxAsync()
Dim oMailIds = Await oClient.SearchAsync(pMailFlag)
Logger.Debug("Fetched [{0}] mail objects.", oMailIds.Count)
Return oMailIds
Else
Logger.Error("No connection to Server. Exiting.")
Return Nothing
End If
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Public Function FetchMail(pMailId As Long) As IMail
Try
If MailSession.Session.Connected Then
Dim oClient As Imap = MailSession.Client
Logger.Debug("Fetching mail with Id [{0}]", pMailId)
Dim oBuffer As Byte() = oClient.GetMessageByUID(pMailId)
Dim oMail As IMail = MailBuilder.CreateFromEml(oBuffer)
Return oMail
Else
Logger.Error("No connection to Server. Exiting.")
Return Nothing
End If
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Public Async Function FetchMailAsync(pMailId As Long) As Task(Of IMail)
Try
If MailSession.Session.Connected Then
Dim oClient As Imap = MailSession.Client
Logger.Debug("Fetching mail with Id [{0}]", pMailId)
Dim oBuffer As Byte() = Await oClient.GetMessageByUIDAsync(pMailId)
Dim oMail As IMail = MailBuilder.CreateFromEml(oBuffer)
Return oMail
Else
Logger.Error("No connection to Server. Exiting.")
Return Nothing
End If
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Public Function DeleteMail(pMailId As Long) As Boolean
Try
If MailSession.Session.Connected Then
Dim oClient As Imap = MailSession.Client
Logger.Debug("Fetching mail with Id [{0}]", pMailId)
oClient.DeleteMessageByUID(pMailId)
Return True
Else
Logger.Error("No connection to Server. Exiting.")
Return False
End If
Catch ex As Exception
Logger.Error(ex)
Return False
End Try
End Function
End Class
End Namespace

View File

@ -0,0 +1,132 @@
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports Limilabs.Client.SMTP
Namespace Mail
Public Class MailSender
Inherits BaseClass
Private Property MailSession As MailSession
Public ReadOnly Property Connected2Server As Boolean
Get
Return MailSession.Session.Connected
End Get
End Property
Public Sub New(pLogConfig As LogConfig)
MyBase.New(pLogConfig)
MailSession = New MailSession(pLogConfig, New Smtp)
Limilabs.Mail.Log.Enabled = pLogConfig.Debug
End Sub
Public Function Connect(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String) As MailSession.SessionInfo
Return MailSession.ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType, New MailSession.MailSessionOptions)
End Function
Public Function Connect(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSession.MailSessionOptions) As MailSession.SessionInfo
Return MailSession.ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType, pOptions)
End Function
Public Function Disconnect() As Boolean
Return MailSession.DisconnectFromServer()
End Function
Public Function SendMail(pSendTo As List(Of String), pSendFrom As String, pSubject As String, pBody As String, pCreationTime As Date, pAttachments As List(Of String), pTest As Boolean) As Boolean
Dim oSuccessfulSends As New List(Of String)
Dim oFailedSends As New List(Of String)
For Each oSendToAddress In pSendTo
Dim oResult = SendMailTo(oSendToAddress, pSendFrom, pSubject, pBody, pCreationTime, pAttachments, pTest)
If oResult = True Then
oSuccessfulSends.Add(oSendToAddress)
Else
oFailedSends.Add(oSendToAddress)
End If
Next
Logger.Debug("Sent [{0}] mails.", pSendTo.Count)
Logger.Debug("Successful [{0}]", oSuccessfulSends.Count)
Logger.Debug("Failed [{0}]", oFailedSends.Count)
Return True
End Function
Private Function SendMailTo(pSendTo As String, pSendFrom As String, pSubject As String, pBody As String, pCreationTime As Date, pAttachments As List(Of String), pTest As Boolean)
Try
Dim oClient As Smtp = DirectCast(MailSession.Client, Smtp)
If IsNothing(oClient) Then
Logger.Warn("Client is nothing! Exiting.")
Return False
End If
Logger.Debug("Preparing to send mail to [{0}]", pSendTo)
Dim oMailBuilder As New Limilabs.Mail.MailBuilder()
oMailBuilder.From.Add(New Limilabs.Mail.Headers.MailBox(pSendFrom))
oMailBuilder.To.Add(New Limilabs.Mail.Headers.MailBox(pSendTo))
oMailBuilder.Subject = pSubject
Logger.Debug("Setting body for mail")
oMailBuilder = SetBody(oMailBuilder, pBody, pCreationTime, pTest)
Logger.Debug("Adding [{0}] attachments to mail", pAttachments.Count)
oMailBuilder = AddAttachments(oMailBuilder, pAttachments)
Logger.Debug("Now sending mail..")
Dim oMail = oMailBuilder.Create()
oClient.SendMessage(oMail)
Logger.Info("Mail to [{0}] has been sent.", pSendTo)
Return True
Catch ex As Exception
Logger.Warn("Error while sending mail to [{0}]", pSendTo)
Logger.Error(ex)
Return False
End Try
End Function
Private Function SetBody(pMailBuilder As Limilabs.Mail.MailBuilder, pBody As String, pCreationTime As Date, pTest As Boolean) As Limilabs.Mail.MailBuilder
If pCreationTime <> Date.MinValue Then
pBody &= $"<p>Creation-time: {pCreationTime}</p>"
End If
If pTest Then
pBody = $"<p>This Is a Testmail!<br/>
The body-text will be replaced within profile!<br/>
Server/Port: {MailSession.Session.Server}/{MailSession.Session.Port}<br/>
User: {MailSession.Session.User}<br/>
Password: XXXX<br/>
Auth Type: {MailSession.Session.AuthType}</p>"
End If
Logger.Debug("Final Mailbody is: [{0}]", pBody)
pMailBuilder.Html = pBody
Return pMailBuilder
End Function
Private Function AddAttachments(pMailBuilder As Limilabs.Mail.MailBuilder, pAttachments As List(Of String)) As Limilabs.Mail.MailBuilder
For Each oAttachment In pAttachments
Try
' Read attachment from disk, add it to Attachments collection
If IO.File.Exists(oAttachment) Then
Logger.Debug("Adding attachment [{0}] to mail.", oAttachment)
pMailBuilder.AddAttachment(oAttachment)
Else
Logger.Warn("Attachment [{0}] does not exist. Skipping.", oAttachment)
End If
Catch ex As Exception
Logger.Warn("Could not read or add attachment [{0}]!", oAttachment)
Logger.Error(ex)
End Try
Next
Return pMailBuilder
End Function
End Class
End Namespace

View File

@ -0,0 +1,322 @@
Imports System.Net.Security
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports Limilabs.Client
Imports Limilabs.Client.IMAP
Imports Limilabs.Client.SMTP
Imports Microsoft.Identity.Client
Namespace Mail
Public Class MailSession
Inherits BaseClass
Public ReadOnly Client As ClientBase
Public ReadOnly OAuth2 As OAuth2
Public Const AUTH_SSL = "SSL"
Public Const AUTH_STARTTLS = "STARTTLS"
Public Const AUTH_SSLTLS = "SSL/TLS"
Public Const AUTH_NONE = "NONE"
Public Const AUTH_OAUTH2 = "OAUTH2"
Private Const SMTP_IGNORED_ERRORS As SslPolicyErrors =
SslPolicyErrors.RemoteCertificateChainErrors Or ' self-signed
SslPolicyErrors.RemoteCertificateNameMismatch ' name mismatch
Private _Session As SessionInfo
Public ReadOnly Property Session As SessionInfo
Get
Return _Session
End Get
End Property
Public Sub New(pLogConfig As LogConfig, pClient As ClientBase)
MyBase.New(pLogConfig)
Client = pClient
End Sub
Public Class SessionInfo
Public Server As String
Public Port As Integer
Public User As String
Public Password As String
Public AuthType As String
Public ClientId As String
Public TenantId As String
Public ClientSecret As String
Public [Error] As Exception
Public Connected As Boolean = False
End Class
Public Class MailSessionOptions
Public Property EnableDefault As Boolean = True
Public Property EnableTls1_1 As Boolean = False
Public Property EnableTls1_2 As Boolean = False
' Not available in .NET 4.6.2
'Public Property EnableTls1_3 As Boolean = False
End Class
''' <summary>
''' Start a connection with the specified server and return the session info.
''' </summary>
''' <param name="pServer"></param>
''' <param name="pPort"></param>
''' <param name="pUser"></param>
''' <param name="pPassword"></param>
''' <param name="pAuthType"></param>
''' <returns></returns>
Public Function ConnectToServer(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSessionOptions) As SessionInfo
Dim oSession = New SessionInfo With {
.Server = pServer,
.Port = pPort,
.User = pUser,
.Password = pPassword,
.AuthType = pAuthType
}
Logger.Debug("Connecting to Server..")
Logger.Debug("Server: [{0}]", oSession.Server)
Logger.Debug("Port: [{0}]", oSession.Port)
Logger.Debug("User: [{0}]", oSession.User)
Logger.Debug("AuthType: [{0}]", oSession.AuthType)
_Session = oSession
Logger.Debug("Initializing Connection with Auth type [{0}].", oSession.AuthType)
Return ConnectToServer(oSession, pOptions)
End Function
Private Function ConnectToServer(pSession As SessionInfo, pOptions As MailSessionOptions) As SessionInfo
AddHandler Client.ServerCertificateValidate, AddressOf Session_ServerCertificateValidate
If pOptions.EnableDefault Then
Logger.Debug("Enabling Default TLS Version")
Client.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.Default
Else
Logger.Debug("Disabling Default TLS Version")
Client.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.None
End If
' Set TLS Version manually if requested
If pOptions.EnableTls1_1 Then
Logger.Debug("Enabling TLS Version 1.1")
Client.SSLConfiguration.EnabledSslProtocols = Client.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls11
End If
If pOptions.EnableTls1_2 Then
Logger.Debug("Enabling TLS Version 1.2")
Client.SSLConfiguration.EnabledSslProtocols = Client.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls12
End If
' This is not available in .NET 4.6.2, only in .NET 4.7/4.8
'If pOptions.EnableTls1_3 Then
' Logger.Debug("Enabling TLS Version 1.3")
' oSession.SSLConfiguration.EnabledSslProtocols = oSession.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls13
'End If
Logger.Debug("Enabled Encryption Protocols: [{0}]", Client.SSLConfiguration.EnabledSslProtocols)
If pSession.AuthType = AUTH_OAUTH2 Then
Try
If TypeOf Client Is Imap Then
Dim oClient As Imap = Client
oClient.ConnectSSL(pSession.Server)
Else
Throw New ApplicationException("Only OAuth2 for IMAP is not yet supported!")
End If
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type OAuth2!")
Logger.Error(ex)
Session.Error = ex
Return Session
End Try
ElseIf pSession.AuthType = AUTH_SSL Then
Try
If pSession.Port = 465 Then
Logger.Debug("Connecting with [ConnectSSL] on [{0}/{1}]", pSession.Server, pSession.Port)
Client.ConnectSSL(pSession.Server, pSession.Port)
Else
Logger.Debug("Connecting with [Connect] on [{0}/{1}]", pSession.Server, pSession.Port)
Client.Connect(pSession.Server, pSession.Port)
End If
Logger.Info("Connection Successful!")
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type SSL!")
Logger.Error(ex)
Session.Error = ex
Return Session
End Try
ElseIf Session.AuthType = AUTH_SSLTLS Or Session.AuthType = AUTH_STARTTLS Then
Try
Logger.Debug("Connecting with [Connect] on [{0}/{1}]", pSession.Server, pSession.Port)
Client.Connect(pSession.Server, pSession.Port)
Logger.Info("Connection Successful!")
Dim oSupportsSTARTTLS As Boolean = SupportsSTARTTLS(Client)
Logger.Debug("Server supports STARTTLS: [{0}]", oSupportsSTARTTLS)
If oSupportsSTARTTLS Then
DoSTARTTLS(Client)
Logger.Info("STARTTLS Successful!")
Else
Logger.Debug("Server does not support STARTTLS. Enabling TLS1.2 instead.")
Client.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.Tls12
End If
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type STARTTLS!")
Logger.Error(ex)
pSession.Error = ex
Return pSession
End Try
Else
Try
Logger.Debug("Unknown Auth type. Using PLAINTEXT connection.")
Client.Connect(pSession.Server, pSession.Port, useSSL:=False)
Logger.Info("Connection Successful!")
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type PLAINTEXT!")
Logger.Error(ex)
pSession.Error = ex
Return pSession
End Try
End If
Try
If pSession.User <> String.Empty Then
Logger.Info("Logging in with user [{0}] and Auth Type [{1}]", pSession.User, pSession.AuthType)
If pSession.AuthType = AUTH_OAUTH2 Then
' SessionInfo.Password will be the access token that was obtained
' in the OAuth2 flow before
DoUseBestLogin_OAuth2(Client, pSession.User, pSession.Password)
Else
DoUseBestLogin_BasicAuth(Client, pSession.User, pSession.Password)
End If
End If
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type [{0}]!", pSession.AuthType)
Logger.Error(ex)
pSession.Error = ex
Return pSession
End Try
pSession.Connected = True
Return pSession
End Function
Public Function DisconnectFromServer() As Boolean
Try
If Client IsNot Nothing Then
Logger.Debug("Closing connection to Server [{0}].", Session.Server)
DoClose(Client)
Session.Connected = False
Logger.Info("Connection to Server [{0}] closed.", Session.Server)
Else
Logger.Debug("No connection currently open. Exiting.")
End If
Return True
Catch ex As Exception
Logger.Error(ex)
Return False
End Try
End Function
Public Async Function TestLogin(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSessionOptions) As Task(Of Boolean)
Dim oInfo = Await ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType, pOptions)
If oInfo.Connected Then
If DisconnectFromServer() Then
Return True
Else
Logger.Warn("Login Test failed while disconnecting.")
Return False
End If
Else
Logger.Warn("Login Test Failed while connecting.")
Return False
End If
End Function
Private Function SupportsSTARTTLS(pClient As ClientBase)
If TypeOf pClient Is Smtp Then
Return DirectCast(pClient, Smtp).SupportedExtensions.Contains(SmtpExtension.StartTLS)
ElseIf TypeOf pClient Is Imap Then
Return DirectCast(pClient, Imap).SupportedExtensions.Contains(ImapExtension.StartTLS)
Else
Logger.Error("Unknown session type: [{0}]", pClient.GetType.ToString)
Return False
End If
End Function
Private Sub DoClose(pClient As ClientBase)
If TypeOf pClient Is Smtp Then
DirectCast(pClient, Smtp).Close()
ElseIf TypeOf pClient Is Imap Then
DirectCast(pClient, Imap).Close()
Else
Logger.Error("Unknown session type: [{0}]", pClient.GetType.ToString)
End If
End Sub
Private Sub DoUseBestLogin_BasicAuth(pClient As ClientBase, pUserName As String, pPassword As String)
If TypeOf pClient Is Smtp Then
DirectCast(pClient, Smtp).UseBestLogin(pUserName, pPassword)
ElseIf TypeOf pClient Is Imap Then
DirectCast(pClient, Imap).UseBestLogin(pUserName, pPassword)
Else
Logger.Error("Unknown session type: [{0}]", pClient.GetType.ToString)
End If
End Sub
Private Sub DoUseBestLogin_OAuth2(pClient As ClientBase, pUserName As String, pAccessToken As String)
If TypeOf pClient Is Imap Then
DirectCast(pClient, Imap).LoginOAUTH2(pUserName, pAccessToken)
Else
Logger.Error("Unknown session type: [{0}]", pClient.GetType.ToString)
End If
End Sub
Private Sub DoSTARTTLS(pClient As ClientBase)
If TypeOf pClient Is Smtp Then
DirectCast(pClient, Smtp).StartTLS()
ElseIf TypeOf pClient Is Imap Then
DirectCast(pClient, Imap).StartTLS()
Else
Logger.Error("Unknown session type: [{0}]", pClient.GetType.ToString)
End If
End Sub
Private Sub Session_ServerCertificateValidate(sender As Object, e As ServerCertificateValidateEventArgs)
' i dont know why it works but it does
If (e.SslPolicyErrors And Not SMTP_IGNORED_ERRORS) = SslPolicyErrors.None Then
e.IsValid = True
Else
e.IsValid = False
End If
End Sub
End Class
End Namespace

53
Messaging/Mail/OAuth2.vb Normal file
View File

@ -0,0 +1,53 @@
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Messaging.Mail.MailSession
Imports Microsoft.Identity.Client
Public Class OAuth2
Inherits BaseClass
Private ReadOnly TenantId As String
Private ReadOnly ClientId As String
Private ReadOnly ClientSecret As String
Public Const O365_SCOPE = "https://outlook.office365.com/.default"
Public Const O365_SERVER = "outlook.office365.com"
Private _AccessToken As String
Public ReadOnly Property AccessToken
Get
Return _AccessToken
End Get
End Property
Public Sub New(pLogConfig As LogConfig, pTenantId As String, pClientId As String, pClientSecret As String)
MyBase.New(pLogConfig)
TenantId = pTenantId
ClientId = pClientId
ClientSecret = pClientSecret
End Sub
Private Async Function GetAccessToken(pSession As SessionInfo) As Task(Of String)
Try
' Create the application, which is defined in
' Microsoft.Identity.Client
Dim oApp = ConfidentialClientApplicationBuilder.
Create(pSession.ClientId).
WithTenantId(pSession.TenantId).
WithClientSecret(pSession.ClientSecret).
Build()
' Request an access token
Dim oScopes = New List(Of String) From {O365_SCOPE}
Dim oResult = Await oApp.
AcquireTokenForClient(oScopes).
ExecuteAsync()
Return oResult.AccessToken
Catch ex As Exception
Logger.Warn("Could not request access token!")
Logger.Error(ex)
Return Nothing
End Try
End Function
End Class

View File

@ -1,294 +0,0 @@
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Base
Imports System.Net.Security
Imports Limilabs.Client.SMTP
Imports Limilabs.Client
Public Class MailSender
Inherits BaseClass
Private Server As String
Private Port As Integer
Private User As String
Private Password As String
Private AuthType As String
Private Session As Smtp = Nothing
Public Connected2Server As Boolean = False
Const SMTP_IGNORED_ERRORS As SslPolicyErrors =
SslPolicyErrors.RemoteCertificateChainErrors Or ' self-signed
SslPolicyErrors.RemoteCertificateNameMismatch ' name mismatch
Public Class MailSenderOptions
Public Property EnableDefault As Boolean = True
Public Property EnableTls1_1 As Boolean = False
Public Property EnableTls1_2 As Boolean = False
' Not available in .NET 4.6.2
'Public Property EnableTls1_3 As Boolean = False
End Class
Public Sub New(pLogConfig As LogConfig)
MyBase.New(pLogConfig)
End Sub
Public Function ConnectToServer(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String) As Boolean
Return ConnectToServer(pServer, pPort, pUser, pPassword, pAuthType)
End Function
''' <summary>
''' Start a connection with the specified server and return the SMTP client. Throws if there was an error.
''' </summary>
''' <param name="pServer"></param>
''' <param name="pPort"></param>
''' <param name="pUser"></param>
''' <param name="pPassword"></param>
''' <param name="pAuthType"></param>
''' <returns></returns>
Public Function ConnectToServer(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSenderOptions) As Boolean
Server = pServer
Port = pPort
User = pUser
Password = pPassword
AuthType = pAuthType
Logger.Debug("Connecting to Server..")
Logger.Debug("SMTP Server: [{0}]", Server)
Logger.Debug("SMTP Port: [{0}]", Port)
Logger.Debug("SMTP User: [{0}]", User)
Logger.Debug("SMTP AuthType: [{0}]", AuthType)
Dim oSession As New Smtp()
AddHandler oSession.ServerCertificateValidate, AddressOf Session_ServerCertificateValidate
Logger.Debug("Initializing Connection with Auth type [{0}].", pAuthType)
If pOptions.EnableDefault Then
Logger.Debug("Enabling Default TLS Version")
oSession.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.Default
Else
Logger.Debug("Disabling Default TLS Version")
oSession.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.None
End If
' Set TLS Version manually if requested
If pOptions.EnableTls1_1 Then
Logger.Debug("Enabling TLS Version 1.1")
oSession.SSLConfiguration.EnabledSslProtocols = oSession.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls11
End If
If pOptions.EnableTls1_2 Then
Logger.Debug("Enabling TLS Version 1.2")
oSession.SSLConfiguration.EnabledSslProtocols = oSession.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls12
End If
' This is not available in .NET 4.6.2, only in .NET 4.7/4.8
'If pOptions.EnableTls1_3 Then
' Logger.Debug("Enabling TLS Version 1.3")
' oSession.SSLConfiguration.EnabledSslProtocols = oSession.SSLConfiguration.EnabledSslProtocols Or Security.Authentication.SslProtocols.Tls13
'End If
Logger.Debug("Enabled Encryption Protocols: [{0}]", oSession.SSLConfiguration.EnabledSslProtocols)
If pAuthType = "SSL" Then
Try
If pPort = 465 Then
Logger.Debug("Connecting with [ConnectSSL] on [{0}/{1}]", pServer, pPort)
oSession.ConnectSSL(pServer)
Else
Logger.Debug("Connecting with [Connect] on [{0}/{1}]", pServer, pPort)
oSession.Connect(pServer, pPort)
End If
Logger.Info("Connection Successful!")
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type SSL!")
Logger.Error(ex)
Return False
End Try
ElseIf AuthType = "SSL/TLS" Or AuthType = "STARTTLS" Then
Try
Logger.Debug("Connecting with [Connect] on [{0}/{1}]", pServer, pPort)
oSession.Connect(pServer, pPort)
Logger.Info("Connection Successful!")
Dim oSupportsSTARTTLS As Boolean = oSession.SupportedExtensions.Contains(Limilabs.Client.SMTP.SmtpExtension.StartTLS)
Logger.Debug("Server supports STARTTLS: [{0}]", oSupportsSTARTTLS)
If oSupportsSTARTTLS Then
oSession.StartTLS()
Logger.Info("STARTTLS Successful!")
Else
Logger.Debug("Server does not support STARTTLS. Enabling TLS1.2 instead.")
oSession.SSLConfiguration.EnabledSslProtocols = Security.Authentication.SslProtocols.Tls12
End If
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type STARTTLS!")
Logger.Error(ex)
Return False
End Try
Else
Try
Logger.Debug("Unknown Auth type. Using PLAINTEXT connection.")
oSession.Connect(pServer)
Logger.Info("Connection Successful!")
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type PLAINTEXT!")
Logger.Error(ex)
Return False
End Try
End If
Try
If pUser <> String.Empty Then
Logger.Info("Logging in with user [{0}]", pUser)
oSession.UseBestLogin(pUser, pPassword)
End If
Catch ex As Exception
Logger.Warn("Error while connecting with Auth type PLAINTEXT!")
Logger.Error(ex)
Return False
End Try
Session = oSession
Connected2Server = True
Return True
End Function
Public Function DisconnectFromServer() As Boolean
Try
If Session IsNot Nothing Then
Logger.Debug("Closing connection to Server [{0}].", Server)
Session.Close()
Session = Nothing
Logger.Info("Connection to Server [{0}] closed.", Server)
Else
Logger.Debug("No connection currently open. Exiting.")
End If
Return True
Catch ex As Exception
Logger.Error(ex)
Return False
End Try
End Function
Private Sub Session_ServerCertificateValidate(sender As Object, e As ServerCertificateValidateEventArgs)
' i dont know why it works but it does
If (e.SslPolicyErrors And Not SMTP_IGNORED_ERRORS) = SslPolicyErrors.None Then
e.IsValid = True
Return
End If
e.IsValid = False
End Sub
Public Function SendMail(pSendTo As List(Of String), pSendFrom As String, pSubject As String, pBody As String, pCreationTime As Date, pAttachments As List(Of String), pTest As Boolean) As Boolean
Dim oSuccessfulSends As New List(Of String)
Dim oFailedSends As New List(Of String)
For Each oSendToAddress In pSendTo
Dim oResult = SendMailTo(Session, oSendToAddress, pSendFrom, pSubject, pBody, pCreationTime, pAttachments, pTest)
If oResult = True Then
oSuccessfulSends.Add(oSendToAddress)
Else
oFailedSends.Add(oSendToAddress)
End If
Next
Logger.Debug("Sent [{0}] mails.", pSendTo.Count)
Logger.Debug("Successful [{0}]", oSuccessfulSends.Count)
Logger.Debug("Failed [{0}]", oFailedSends.Count)
Return True
End Function
Private Function SendMailTo(pSession As Smtp, pSendTo As String, pSendFrom As String, pSubject As String, pBody As String, pCreationTime As Date, pAttachments As List(Of String), pTest As Boolean)
Try
If IsNothing(pSession) Then
Logger.Info("ATTENTION-ERROR: pSession is nothing!")
Return False
End If
Logger.Debug("Preparing to send mail to [{0}]", pSendTo)
Dim oMailBuilder As New Limilabs.Mail.MailBuilder()
oMailBuilder.From.Add(New Limilabs.Mail.Headers.MailBox(pSendFrom))
oMailBuilder.To.Add(New Limilabs.Mail.Headers.MailBox(pSendTo))
oMailBuilder.Subject = pSubject
Logger.Debug("Setting body for mail")
SetBody(oMailBuilder, pBody, pCreationTime, pTest)
Logger.Debug("Adding [{0}] attachments to mail", pAttachments.Count)
oMailBuilder = AddAttachments(oMailBuilder, pAttachments)
Logger.Debug("Now sending mail..")
If IsNothing(oMailBuilder) Then
Logger.Info("ATTENTION-ERROR: oMailBuilder is nothing!")
Return False
End If
Dim oMail = oMailBuilder.Create()
pSession.SendMessage(oMail)
Logger.Info("Mail to [{0}] has been sent.", pSendTo)
Return True
Catch ex As Exception
Logger.Warn("Error while sending mail to [{0}]", pSendTo)
Logger.Error(ex)
Return False
End Try
End Function
Private Function SetBody(pMailBuilder As Limilabs.Mail.MailBuilder, pBody As String, pCreationTime As Date, pTest As Boolean) As Limilabs.Mail.MailBuilder
If pCreationTime <> Date.MinValue Then
pBody &= $"<p>Creation-time: {pCreationTime}</p>"
End If
If pTest Then
pBody = $"<p>This Is a Testmail!<br/>
The body-text will be replaced within profile!<br/>
Server/Port: {Server}/{Port}<br/>
User: {User}<br/>
Password: XXXX<br/>
Auth Type: {AuthType}</p>"
End If
Logger.Debug("Final Mailbody is: [{0}]", pBody)
pMailBuilder.Html = pBody
Return pMailBuilder
End Function
Private Function AddAttachments(pMailBuilder As Limilabs.Mail.MailBuilder, pAttachments As List(Of String)) As Limilabs.Mail.MailBuilder
For Each oAttachment In pAttachments
Try
' Read attachment from disk, add it to Attachments collection
If IO.File.Exists(oAttachment) Then
Logger.Debug("Adding attachment [{0}] to mail.", oAttachment)
pMailBuilder.AddAttachment(oAttachment)
Else
Logger.Warn("Attachment [{0}] does not exist. Skipping.", oAttachment)
End If
Catch ex As Exception
Logger.Warn("Could not read or add attachment [{0}]!", oAttachment)
Logger.Error(ex)
End Try
Next
Return pMailBuilder
End Function
End Class

View File

@ -49,16 +49,25 @@
<EmbedInteropTypes>False</EmbedInteropTypes> <EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Identity.Client, Version=4.55.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Identity.Client.4.55.0\lib\net461\Microsoft.Identity.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Abstractions, Version=6.22.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Abstractions.6.22.0\lib\net461\Microsoft.IdentityModel.Abstractions.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath> <HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" /> <Reference Include="System.Transactions" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@ -81,6 +90,9 @@
<Compile Include="EventBus.vb" /> <Compile Include="EventBus.vb" />
<Compile Include="Email2.vb" /> <Compile Include="Email2.vb" />
<Compile Include="Limilab.vb" /> <Compile Include="Limilab.vb" />
<Compile Include="Mail\MailFetcher.vb" />
<Compile Include="Mail\MailSession.vb" />
<Compile Include="Mail\OAuth2.vb" />
<Compile Include="My Project\AssemblyInfo.vb" /> <Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb"> <Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
@ -97,7 +109,7 @@
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile> </Compile>
<Compile Include="MailSender.vb" /> <Compile Include="Mail\MailSender.vb" />
<Compile Include="WCF\Binding.vb" /> <Compile Include="WCF\Binding.vb" />
<Compile Include="WCF\Channel.vb" /> <Compile Include="WCF\Channel.vb" />
<Compile Include="WCF\Constants.vb" /> <Compile Include="WCF\Constants.vb" />

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Microsoft.Identity.Client" version="4.55.0" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Abstractions" version="6.22.0" targetFramework="net462" />
<package id="NLog" version="5.0.5" targetFramework="net461" /> <package id="NLog" version="5.0.5" targetFramework="net461" />
<package id="S22.Imap" version="3.6.0.0" targetFramework="net461" /> <package id="S22.Imap" version="3.6.0.0" targetFramework="net461" />
</packages> </packages>