From 77d8a0825f3cebbd26b1c032ff485764e421b514 Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Tue, 22 Aug 2023 08:38:46 +0200 Subject: [PATCH] Messaging: WIP MailFetcher, MailSession --- Messaging/Limilab.vb | 2 +- Messaging/Mail/MailFetcher.vb | 177 +++++++++++++++++++ Messaging/Mail/MailSender.vb | 132 ++++++++++++++ Messaging/Mail/MailSession.vb | 322 ++++++++++++++++++++++++++++++++++ Messaging/Mail/OAuth2.vb | 53 ++++++ Messaging/MailSender.vb | 294 ------------------------------- Messaging/Messaging.vbproj | 14 +- Messaging/packages.config | 2 + 8 files changed, 700 insertions(+), 296 deletions(-) create mode 100644 Messaging/Mail/MailFetcher.vb create mode 100644 Messaging/Mail/MailSender.vb create mode 100644 Messaging/Mail/MailSession.vb create mode 100644 Messaging/Mail/OAuth2.vb delete mode 100644 Messaging/MailSender.vb diff --git a/Messaging/Limilab.vb b/Messaging/Limilab.vb index 99907410..0d567c14 100644 --- a/Messaging/Limilab.vb +++ b/Messaging/Limilab.vb @@ -66,7 +66,7 @@ Public Class Limilab End Function Private Function LOG_Limilab(Log_enabled As Boolean) As Boolean - Mail.Log.Enabled = Log_enabled + Log.Enabled = Log_enabled End Function ''' diff --git a/Messaging/Mail/MailFetcher.vb b/Messaging/Mail/MailFetcher.vb new file mode 100644 index 00000000..fc07ee0b --- /dev/null +++ b/Messaging/Mail/MailFetcher.vb @@ -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 + diff --git a/Messaging/Mail/MailSender.vb b/Messaging/Mail/MailSender.vb new file mode 100644 index 00000000..c3bf7eb0 --- /dev/null +++ b/Messaging/Mail/MailSender.vb @@ -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 &= $"

Creation-time: {pCreationTime}

" + End If + + If pTest Then + pBody = $"

This Is a Testmail!
+ The body-text will be replaced within profile!
+ Server/Port: {MailSession.Session.Server}/{MailSession.Session.Port}
+ User: {MailSession.Session.User}
+ Password: XXXX
+ Auth Type: {MailSession.Session.AuthType}

" + 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 + diff --git a/Messaging/Mail/MailSession.vb b/Messaging/Mail/MailSession.vb new file mode 100644 index 00000000..fafa086e --- /dev/null +++ b/Messaging/Mail/MailSession.vb @@ -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 + + ''' + ''' Start a connection with the specified server and return the session info. + ''' + ''' + ''' + ''' + ''' + ''' + ''' + 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 + diff --git a/Messaging/Mail/OAuth2.vb b/Messaging/Mail/OAuth2.vb new file mode 100644 index 00000000..54dd58fb --- /dev/null +++ b/Messaging/Mail/OAuth2.vb @@ -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 diff --git a/Messaging/MailSender.vb b/Messaging/MailSender.vb deleted file mode 100644 index e6c0f564..00000000 --- a/Messaging/MailSender.vb +++ /dev/null @@ -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 - - ''' - ''' Start a connection with the specified server and return the SMTP client. Throws if there was an error. - ''' - ''' - ''' - ''' - ''' - ''' - ''' - 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 &= $"

Creation-time: {pCreationTime}

" - End If - - If pTest Then - pBody = $"

This Is a Testmail!
- The body-text will be replaced within profile!
- Server/Port: {Server}/{Port}
- User: {User}
- Password: XXXX
- Auth Type: {AuthType}

" - 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 diff --git a/Messaging/Messaging.vbproj b/Messaging/Messaging.vbproj index 666effc2..59d8ef79 100644 --- a/Messaging/Messaging.vbproj +++ b/Messaging/Messaging.vbproj @@ -49,16 +49,25 @@ False + + ..\packages\Microsoft.Identity.Client.4.55.0\lib\net461\Microsoft.Identity.Client.dll + + + ..\packages\Microsoft.IdentityModel.Abstractions.6.22.0\lib\net461\Microsoft.IdentityModel.Abstractions.dll + ..\packages\NLog.5.0.5\lib\net46\NLog.dll + + + @@ -81,6 +90,9 @@ + + + True @@ -97,7 +109,7 @@ Settings.settings True - + diff --git a/Messaging/packages.config b/Messaging/packages.config index 5f73593d..957b4e14 100644 --- a/Messaging/packages.config +++ b/Messaging/packages.config @@ -1,5 +1,7 @@  + + \ No newline at end of file