Messaging: WIP MailFetcher, MailSession
This commit is contained in:
parent
f6046aec54
commit
77d8a0825f
@ -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
|
||||
|
||||
''' <summary>
|
||||
|
||||
177
Messaging/Mail/MailFetcher.vb
Normal file
177
Messaging/Mail/MailFetcher.vb
Normal 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
|
||||
|
||||
132
Messaging/Mail/MailSender.vb
Normal file
132
Messaging/Mail/MailSender.vb
Normal 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
|
||||
|
||||
322
Messaging/Mail/MailSession.vb
Normal file
322
Messaging/Mail/MailSession.vb
Normal 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
53
Messaging/Mail/OAuth2.vb
Normal 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
|
||||
@ -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
|
||||
@ -49,16 +49,25 @@
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<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">
|
||||
<HintPath>..\packages\NLog.5.0.5\lib\net46\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IdentityModel" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@ -81,6 +90,9 @@
|
||||
<Compile Include="EventBus.vb" />
|
||||
<Compile Include="Email2.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\Application.Designer.vb">
|
||||
<AutoGen>True</AutoGen>
|
||||
@ -97,7 +109,7 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Compile Include="MailSender.vb" />
|
||||
<Compile Include="Mail\MailSender.vb" />
|
||||
<Compile Include="WCF\Binding.vb" />
|
||||
<Compile Include="WCF\Channel.vb" />
|
||||
<Compile Include="WCF\Constants.vb" />
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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="S22.Imap" version="3.6.0.0" targetFramework="net461" />
|
||||
</packages>
|
||||
Loading…
x
Reference in New Issue
Block a user