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 ConnectToServerWithBasicAuth(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 Public Function ConnectToServerWithO365OAuth(pUser As String, pClientId As String, pTenantId As String, pClientSecret As String, pOptions As MailSessionOptions) As SessionInfo Dim oSession = New SessionInfo With { .Server = OAuth2.O365_SERVER, .ClientId = pClientId, .ClientSecret = pClientSecret, .TenantId = pTenantId, .User = pUser, .AuthType = AUTH_OAUTH2 } Logger.Debug("Connecting to Server..") Logger.Debug("Server: [{0}]", oSession.Server) Logger.Debug("User: [{0}]", oSession.User) Logger.Debug("ClientId: [{0}]", oSession.ClientId) Logger.Debug("TenantId: [{0}]", oSession.TenantId) 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 Logger.Debug("Connecting with [OAuth2/ConnectSSL] on [{0}]", pSession.Server) oClient.ConnectSSL(pSession.Server) Else Throw New ApplicationException("Only OAuth2 for IMAP is not yet supported!") End If Catch ex As Exception Logger.Warn("Unexpected Error in ConnectToServer with Auth type OAuth2!") Logger.Error(ex) Session.Error = ex Return Session End Try ElseIf pSession.AuthType = AUTH_SSL Then Try ' Port 465 ist der SMTP-SSL-Port, wird bei der WISAG verwendet, aber veraltet ' Port 993 ist der IMAP-SSL-Port, zum Abholen der Mails If pSession.Port = 465 Or pSession.Port = 993 Then Logger.Debug("Connecting with [SSL/ConnectSSL] on [{0}/{1}]", pSession.Server, pSession.Port) Client.ConnectSSL(pSession.Server, pSession.Port) Else Logger.Debug("Connecting with [SSL/Connect] on [{0}/{1}]", pSession.Server, pSession.Port) Client.Connect(pSession.Server, pSession.Port) End If Logger.Debug("Connection (AUTH_SSL) Successful!") Catch ex As Exception Logger.Warn("Unexpected Error in ConnectToServer with Auth type SSL!") Logger.Warn($"Error-message: {ex.Message}") Logger.Error(ex) Session.Error = ex Return Session End Try ElseIf Session.AuthType = AUTH_SSLTLS Or Session.AuthType = AUTH_STARTTLS Then Try If pSession.Port = 993 Then Logger.Debug("Connecting with [STARTTLS/ConnectSSL] on [{0}/{1}]", pSession.Server, pSession.Port) Client.ConnectSSL(pSession.Server, pSession.Port) Else Logger.Debug("Connecting with [STARTTLS/Connect] on [{0}/{1}]", pSession.Server, pSession.Port) Client.Connect(pSession.Server, pSession.Port) End If Logger.Debug("Connection (AUTH_SSLTLS or AUTH_STARTTLS) Successful!") Dim oSupportsSTARTTLS As Boolean = SupportsSTARTTLS(Client) Logger.Debug("Server supports STARTTLS: [{0}]", oSupportsSTARTTLS) If oSupportsSTARTTLS Then DoSTARTTLS(Client) Logger.Debug("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("Unexpected Error in ConnectToServer with Auth type STARTTLS!") Logger.Error(ex) pSession.Error = ex Return pSession End Try Else Try Logger.Debug("Auth type [{0}]. Using PLAINTEXT connection.", Session.AuthType) Client.Connect(pSession.Server, pSession.Port, useSSL:=False) Catch ex As Exception Logger.Warn("Unexpected Error in ConnectToServer with Auth type [{0}]!", Session.AuthType) 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) Else DoUseBestLogin_BasicAuth(Client, pSession.User, pSession.Password) End If End If Catch ex As Exception Logger.Warn("Unexpected Error in ConnectToServer 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 Function TestLogin(pServer As String, pPort As Integer, pUser As String, pPassword As String, pAuthType As String, pOptions As MailSessionOptions) As Boolean Dim oInfo = ConnectToServerWithBasicAuth(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) Logger.Debug("Logging in with Simple Auth") 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, pSession As SessionInfo) Logger.Debug("Logging in with O365 OAuth2") If TypeOf pClient Is Imap Then Dim oOAuth = New OAuth2(LogConfig, pSession.TenantId, pSession.ClientId, pSession.ClientSecret) Dim oAccessToken = oOAuth.GetAccessToken() DirectCast(pClient, Imap).LoginOAUTH2(pSession.User, oAccessToken) 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