diff --git a/GUIs.Test.TestGUI/frmEmail.vb b/GUIs.Test.TestGUI/frmEmail.vb index af9c10d4..26568cf3 100644 --- a/GUIs.Test.TestGUI/frmEmail.vb +++ b/GUIs.Test.TestGUI/frmEmail.vb @@ -7,28 +7,34 @@ Public Class frmEmail Private Email As Email2 Private Sub frmEmail_Load(sender As Object, e As EventArgs) Handles Me.Load - Logconfig = New LogConfig(LogConfig.PathType.Temp) + Logconfig = New LogConfig(LogConfig.PathType.Temp, ProductName:="TestGUI.IMAP") + Logconfig.Debug = True Email = New Email2(Logconfig) End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click - Dim oResult = Email.Test_Login(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSLTLS) + Using oClient = Email.New_IMAPConnection(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSL) + Dim oResult = Email.Test_IMAPLogin(oClient, "Inbox") - If oResult = True Then - AddLog($"Connection to {txtServer.Text} successful.") - Else - AddLog($"Connection to {txtServer.Text} failed!") - End If + If oResult = True Then + AddLog($"Connection to {txtServer.Text} successful.") + Else + AddLog($"Connection to {txtServer.Text} failed!") + End If + End Using End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click - Dim oMessages = Email.Get_Messages(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSLTLS, "Inbox") - AddLog($"Found {oMessages.Count} Messages!") - For Each oMessage In oMessages - AddLog(oMessage.MessageID) - Next + Using oClient = Email.New_IMAPConnection(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSL) + Dim oMessages = Email.Get_IMAPMessages(oClient, "Inbox") + AddLog($"Found {oMessages.Count} Messages!") + + For Each oMessage In oMessages + AddLog(oMessage.MessageID) + Next + End Using End Sub Private Sub AddLog(pMessage) @@ -36,22 +42,24 @@ Public Class frmEmail End Sub Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click - Dim oMessageId As String = txtMessageID.Text - Dim oMail As IMail = Email.Get_Message(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSLTLS, oMessageId, "Inbox") - Dim oFilename As String = IO.Path.GetTempFileName - oMail.Save(oFilename) + Using oClient = Email.New_IMAPConnection(txtServer.Text, txtUser.Text, txtPassword.Text, Email2.EmailSecurity.SSL) + Dim oMessageId As String = txtMessageID.Text + Dim oMail As IMail = Email.Get_IMAPMessage(oClient, oMessageId, "Inbox") + Dim oFilename As String = IO.Path.GetTempFileName + oMail.Save(oFilename) - AddLog($"Mail saved to {oFilename}") + AddLog($"Mail saved to {oFilename}") - Dim oEmailTempPath = Email.Remove_AttachmentsFromEmail(oFilename) + Dim oEmailTempPath = Email.Remove_AttachmentsFromEmail(oFilename) - AddLog($"Mail without attachments saved to {oEmailTempPath}") + AddLog($"Mail without attachments saved to {oEmailTempPath}") - Dim oAttachments As List(Of String) = Email.Save_AttachmentsToDisk(oFilename) + Dim oAttachments As List(Of String) = Email.Save_AttachmentsToDisk(oFilename) - For Each oAttachment In oAttachments - AddLog($"Attachmen saved to {oAttachment}") - Next + For Each oAttachment In oAttachments + AddLog($"Attachmen saved to {oAttachment}") + Next + End Using End Sub Private Sub ListBox1_SelectedValueChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedValueChanged diff --git a/Modules.Messaging/Email2.vb b/Modules.Messaging/Email2.vb index 6a29ccb9..f3bc043d 100644 --- a/Modules.Messaging/Email2.vb +++ b/Modules.Messaging/Email2.vb @@ -1,168 +1,238 @@ -Imports DigitalData.Modules.Logging +Imports System.IO +Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Filesystem Imports Limilabs.Mail -Imports Limilabs.Client.IMAP Imports Limilabs.Mail.MIME -Imports System.IO Imports Limilabs.Mail.MSG +Imports Limilabs.Client.IMAP +Imports Limilabs.Client.SMTP +Imports Limilabs.Client Public Class Email2 Private ReadOnly Logger As Logger + Private ReadOnly LoggerMail As Logger Private ReadOnly LogConfig As LogConfig Private ReadOnly FileEx As Filesystem.File Private ReadOnly MailBuilder As New MailBuilder() + Private DisableExcessiveLogging As Boolean = False + Public Sub New(pLogConfig As LogConfig) LogConfig = pLogConfig Logger = pLogConfig.GetLogger() + LoggerMail = pLogConfig.GetLogger("Limilabs.Mail") FileEx = New Filesystem.File(pLogConfig) + + ' Turn on internal Mail.dll logging + Limilabs.Mail.Log.Enabled = True + + AddHandler Limilabs.Mail.Log.WriteLine, AddressOf ProcessMailLog End Sub Public Enum EmailSecurity - SSLTLS - STARTTLS + SSL + START_TLS End Enum - Private Function Get_PortForSecurityOption(pSecurity As EmailSecurity) As Integer - If pSecurity = EmailSecurity.STARTTLS Then - Return 143 - Else ' EmailSecurity.SSL - Return 993 + Private Sub ProcessMailLog(pMessage As String) + If DisableExcessiveLogging = False Then + LoggerMail.Debug(pMessage) End If - End Function + End Sub - Private Function New_Connection(pIMAP As Imap, pServer As String, pUsername As String, pPassword As String, pPort As Integer, pSecurity As EmailSecurity) As Boolean + Public Function New_SMTPConnection(pSMTP As Smtp, pServer As String, pUsername As String, pPassword As String, pSecurity As EmailSecurity, Optional pPort As Integer = 0) As Smtp Try - Logger.Debug("Connecting to IMAP server [{0}]", pServer) + Logger.Info("Connecting to SMTP server [{0}:{1}] with user [{2}]", pServer, pPort, pUsername) + + Dim oPort As Integer + If pPort = 0 Then + If pSecurity = EmailSecurity.SSL Then + oPort = Smtp.DefaultSSLPort + + Else + oPort = Smtp.DefaultPort + + End If + + Else + oPort = pPort - Dim oPort As Integer = pPort - If oPort = 0 Then - oPort = Get_PortForSecurityOption(pSecurity) End If Logger.Debug("Using Port [{0}] for connection", oPort) - If pSecurity = EmailSecurity.STARTTLS Then - Logger.Debug("Using STARTTLS as Security Option") - pIMAP.Connect(pServer, oPort) - pIMAP.StartTLS() - Else + If pSecurity = EmailSecurity.SSL Then Logger.Debug("Using SSL/TLS as Security Option") - pIMAP.ConnectSSL(pServer, oPort) + pSMTP.ConnectSSL(pServer, oPort) + Else + Logger.Debug("Using STARTTLS as Security Option") + pSMTP.Connect(pServer, oPort) + pSMTP.StartTLS() End If - Logger.Debug("Connection to IMAP Server [{0}] established!", pServer) + Logger.Debug("Connection to SMTP Server [{0}] established!", pServer) Logger.Debug("Logging in with user [{0}]", pUsername) - pIMAP.UseBestLogin(pUsername, pPassword) + pSMTP.UseBestLogin(pUsername, pPassword) - Return True + Return pSMTP Catch ex As Exception Logger.Warn("Could not connect to server [{0}] with user [{1}]", pServer, pUsername) Logger.Error(ex) - Return False + Return pSMTP End Try End Function - Public Function Test_Login(pServer As String, pUsername As String, pPassword As String, pSecurity As EmailSecurity, Optional pFolder As String = "Inbox", Optional pPort As Integer = 0) As Boolean - Logger.Debug("Testing Login to Server [{0}:{1}] with user [{2}]", pServer, pPort, pUsername) - + Public Function New_IMAPConnection(pServer As String, pUsername As String, pPassword As String, pSecurity As EmailSecurity, Optional pPort As Integer = 0) As Imap Try - Using oClient As New Imap() - If New_Connection(oClient, pServer, pUsername, pPassword, pPort, pSecurity) Then - Logger.Debug("Fetching Inbox") - Dim oStatus As FolderStatus = oClient.SelectInbox() + Logger.Info("Connecting to IMAP server [{0}:{1}] with user [{2}]", pServer, pPort, pUsername) - Logger.Debug("Test finished!") - oClient.Close() + Dim oIMAP As New Imap() + Dim oPort As Integer + If pPort = 0 Then + If pSecurity = EmailSecurity.SSL Then + oPort = Imap.DefaultSSLPort + + Else + oPort = Imap.DefaultPort End If - End Using + Else + oPort = pPort + + End If + Logger.Debug("Using Port [{0}] for connection", oPort) + + If pSecurity = EmailSecurity.SSL Then + Logger.Debug("Using SSL/TLS as Security Option") + oIMAP.ConnectSSL(pServer, oPort) + Else + Logger.Debug("Using STARTTLS as Security Option") + oIMAP.Connect(pServer, oPort) + oIMAP.StartTLS() + End If + Logger.Debug("Connection to IMAP Server [{0}] established!", pServer) + + Logger.Debug("Logging in with user [{0}]", pUsername) + oIMAP.UseBestLogin(pUsername, pPassword) + + Return oIMAP + + Catch ex As Exception + Logger.Warn("Could not connect to server [{0}] with user [{1}]", pServer, pUsername) + Logger.Error(ex) + Throw ex + + End Try + End Function + + Public Function Test_IMAPLogin(pClient As Imap, Optional pFolder As String = "Inbox") As Boolean + Logger.Info("Testing Login to IMAP Server") + + Try + Logger.Debug("Fetching Inbox") + Dim oStatus As FolderStatus = pClient.SelectInbox() + + Logger.Debug("Test succeeded!") Return True Catch ex As Exception - Logger.Warn("Login failed for server [{0}:{1}] with user [{2}]!", pServer, pPort, pUsername) + Logger.Warn("Login failed for IMAP server!") Logger.Error(ex) Return False End Try End Function - Public Function Get_Messages(pServer As String, pUsername As String, pPassword As String, pSecurity As EmailSecurity, Optional pFolder As String = "Inbox", Optional pPort As Integer = 0) As List(Of IMail) - Logger.Debug("Fetching Messages from Server [{0}:{1}] with user [{2}]", pServer, pPort, pUsername) + Public Function Test_SMTPLogin(pClient As Smtp) As Boolean + Logger.Info("Testing Login to IMAP Server") + Try + Dim oExtensions = pClient.SupportedExtensions() + Dim oExtensionArray = oExtensions.Select(Function(ex) ex.ToString).ToArray + Logger.Debug("Supported Extensions: ") + Logger.Debug(String.Join(", ", oExtensionArray)) + Logger.Debug("Test succeeded!") + + Return True + Catch ex As Exception + Logger.Warn("Login failed for SMTP server!") + Logger.Error(ex) + + Return False + End Try + End Function + + Public Function Get_IMAPMessages(pClient As Imap, Optional pFolder As String = "Inbox") As List(Of IMail) + Logger.Info("Fetching Messages in Folder [{0}]", pFolder) Dim oMails As New List(Of IMail) Try - Using oClient As New Imap() - If New_Connection(oClient, pServer, pUsername, pPassword, pPort, pSecurity) Then - Logger.Debug("Fetching Folder [{0}]", pFolder) - Dim oStatus As FolderStatus = oClient.Select(pFolder) + Logger.Debug("Fetching Folder [{0}]", pFolder) + Dim oStatus As FolderStatus = pClient.Select(pFolder) - Logger.Debug("Fetching Unseen UUIDs") - Dim oUUIDs As List(Of Long) = oClient.Search(Flag.Unseen) - Dim oMailBuilder As New MailBuilder() + Logger.Debug("Fetching Unseen UUIDs") - Logger.Debug("Fetching Unseen Mails") - For Each oUUID As Long In oUUIDs - Dim oEmlFile As Byte() = oClient.GetMessageByUID(oUUID) - Dim oEmail As IMail = oMailBuilder.CreateFromEml(oEmlFile) - oMails.Add(oEmail) + DisableExcessiveLogging = True + Dim oUUIDs As List(Of Long) = pClient.Search(Flag.Unseen) + DisableExcessiveLogging = False - Next + Logger.Debug("Fetching Unseen Mails") + For Each oUUID As Long In oUUIDs + DisableExcessiveLogging = True + Dim oEmlFile As Byte() = pClient.GetMessageByUID(oUUID) + DisableExcessiveLogging = False - Logger.Debug("Emails fetched!") - oClient.Close() + Dim oEmail As IMail = MailBuilder.CreateFromEml(oEmlFile) + oMails.Add(oEmail) - End If + Next - End Using + Logger.Debug("Emails fetched!") Return oMails Catch ex As Exception - Logger.Warn("Login failed for server [{0}:{1}] with user [{2}]!", pServer, pPort, pUsername) + Logger.Warn("Fetching messages for folder [{0}] failed!", pFolder) Logger.Error(ex) Return New List(Of IMail) End Try End Function - Public Function Get_Message(pServer As String, pUsername As String, pPassword As String, pSecurity As EmailSecurity, pMessageId As String, Optional pFolder As String = "Inbox", Optional pPort As Integer = 0) As IMail - Logger.Debug("Fetching Message [{0}] from Server [{1}:{2}] with user [{3}]", pMessageId, pServer, pPort, pUsername) + Public Function Get_IMAPMessage(pClient As Imap, pMessageId As String, Optional pFolder As String = "Inbox") As IMail + Logger.Info("Fetching Message [{0}]", pMessageId) Dim oMail As IMail = Nothing Try - Using oClient As New Imap() - If New_Connection(oClient, pServer, pUsername, pPassword, pPort, pSecurity) Then - Logger.Debug("Fetching Folder [{0}]", pFolder) - Dim oStatus As FolderStatus = oClient.Select(pFolder) + Logger.Debug("Fetching Folder [{0}]", pFolder) + Dim oStatus As FolderStatus = pClient.Select(pFolder) - Logger.Debug("Fetching UUIDs") - Dim oUUIDs As List(Of Long) = oClient.Search(Flag.All) - Logger.Debug("Fetching Mails") - Dim oInfos As List(Of MessageInfo) = oClient.GetMessageInfoByUID(oUUIDs) + Logger.Debug("Fetching UUIDs") + Dim oUUIDs As List(Of Long) = pClient.Search(Flag.All) + Logger.Debug("Fetching Mails") - Dim oMailInfo = oInfos.Where(Function(i) i.Envelope.MessageID = pMessageId).FirstOrDefault() + DisableExcessiveLogging = True + Dim oInfos As List(Of MessageInfo) = pClient.GetMessageInfoByUID(oUUIDs) + DisableExcessiveLogging = False - If oMailInfo IsNot Nothing Then - Dim oMailData As Byte() = oClient.GetMessageByUID(oMailInfo.UID) - oMail = MailBuilder.CreateFromEml(oMailData) - End If + Dim oMailInfo = oInfos.Where(Function(i) i.Envelope.MessageID = pMessageId).FirstOrDefault() - Logger.Debug("Emails fetched!") - oClient.Close() + If oMailInfo IsNot Nothing Then + DisableExcessiveLogging = True + Dim oMailData As Byte() = pClient.GetMessageByUID(oMailInfo.UID) + DisableExcessiveLogging = False + oMail = MailBuilder.CreateFromEml(oMailData) + End If - End If - - End Using + Logger.Debug("Email fetched!") Return oMail Catch ex As Exception - Logger.Warn("Login failed for server [{0}:{1}] with user [{2}]!", pServer, pPort, pUsername) + Logger.Warn("Fetching message with MessageID [{0}] failed!", pMessageId) Logger.Error(ex) Return Nothing @@ -170,15 +240,54 @@ Public Class Email2 End Try End Function + Public Function Send_SMTPMessage(pClient As Smtp, pSender As String, pReceiver As String, pSubject As String, pBody As String) As Boolean + Logger.Info("Sending Message with Subject [{0}]", pSubject) + + Try + Dim oBuilder As New MailBuilder() + oBuilder.From.Add(New Headers.MailBox(pSender)) + oBuilder.To.Add(New Headers.MailBox(pReceiver)) + oBuilder.Subject = pSubject + oBuilder.Text = pBody + + Dim oMail As IMail = oBuilder.Create() + Dim oResult As ISendMessageResult = pClient.SendMessage(oMail) + + If oResult.Status = SendMessageStatus.Success Then + Logger.Debug("Message sent successful!") + Return True + + Else + Logger.Warn("Message sending failed. Status: [{0}]", oResult.Status) + Return False + + End If + + Catch ex As Exception + Logger.Warn("Message sending failed.") + Logger.Error(ex) + Return False + + End Try + End Function + + ''' + ''' Removes all attachments from an EML file and saves it to a temporary file. + ''' + ''' The EML file to process. + ''' The optional suffix to add to the original filename. + ''' The path of the new EML without attachments. Public Function Remove_AttachmentsFromEmail(pFileName As String, Optional pSuffix As String = "") As String Try - Dim oTempFileName As String = Path.Combine(Path.GetTempPath(), AddFilenameSuffix(pFileName, pSuffix)) + Dim oTempPath As String = Path.Combine(Path.GetTempPath(), Add_FilenameSuffix(pFileName, pSuffix)) + Dim oCleanedPath As String = FileEx.GetCleanFilename(oTempPath) + Dim oVersionedPath As String = FileEx.GetVersionedFilename(oCleanedPath) Dim oMail = MailBuilder.CreateFromEmlFile(pFileName) oMail.RemoveAttachments() - oMail.Save(oTempFileName) + oMail.Save(oVersionedPath) - Return oTempFileName + Return oVersionedPath Catch ex As Exception Logger.Error(ex) @@ -186,6 +295,10 @@ Public Class Email2 End Try End Function + Public Function Load_Email(pFileName As String) As IMail + Return MailBuilder.CreateFromEmlFile(pFileName) + End Function + Public Function Save_AttachmentsToDisk(pFileName As String) As List(Of String) Try Dim oAttachmentPaths As New List(Of String) @@ -212,7 +325,7 @@ Public Class Email2 End Try End Function - Public Function ConvertMsgToEml(pEmailFileName As String) As String + Public Function Convert_MsgToEml(pEmailFileName As String) As String Dim oInfo As New FileInfo(pEmailFileName) If oInfo.Extension.ToUpper = ".EML" Then @@ -229,38 +342,35 @@ Public Class Email2 Private Function DoConvertMsgToEmlFile(pMsgFile As String) As String Try + Dim oTempPath As String = IO.Path.GetTempPath() Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(pMsgFile) - Dim oEmlPath As String = $"{oFileNameWithoutExtension}.eml" + Dim oEmlPath As String = IO.Path.Combine(oTempPath, $"{oFileNameWithoutExtension}.eml") + Dim oVersionedPath As String = FileEx.GetVersionedFilename(oEmlPath) Using oConverter As New MsgConverter(pMsgFile) Dim oEmail As IMail = oConverter.CreateMessage() Dim oData As Byte() = oEmail.Render() - oEmail.Save(oEmlPath) + oEmail.Save(oVersionedPath) + End Using - Return oEmlPath + Return oVersionedPath Catch ex As Exception - Return Nothing + Logger.Warn("Converting Msg to Eml file failed!") + Logger.Error(ex) + Return Nothing End Try End Function - Private Function GetTempFileNameWithExtension(pExtension As String) As String - Dim oTempFileName As String = IO.Path.GetTempFileName() - Dim oInfo As New IO.FileInfo(oTempFileName) - Dim oExtension As String = IIf(pExtension.StartsWith("."), pExtension, $".{pExtension}") - Dim oNewFileName = IO.Path.Combine(oInfo.DirectoryName, oInfo.Name.Replace(oInfo.Extension, oExtension)) - - Return oNewFileName - End Function - - Private Function AddFilenameSuffix(pFilename As String, pSuffix As String) + Private Function Add_FilenameSuffix(pFilename As String, pSuffix As String) Dim oFileInfo As New FileInfo(pFilename) Dim oExtension As String = oFileInfo.Extension Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(pFilename) Return $"{oFileNameWithoutExtension}{pSuffix}{oExtension}" + End Function End Class