diff --git a/Stundensheet.xlsx b/Stundensheet.xlsx index da412df0..6d07817d 100644 Binary files a/Stundensheet.xlsx and b/Stundensheet.xlsx differ diff --git a/nsw/Source/bsmd.ExcelReadService/App.config b/nsw/Source/bsmd.ExcelReadService/App.config index d31d17f2..b099a546 100644 --- a/nsw/Source/bsmd.ExcelReadService/App.config +++ b/nsw/Source/bsmd.ExcelReadService/App.config @@ -35,9 +35,6 @@ - - Data Source=(localdb)\Projects;Initial Catalog=nsw;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False - 300 @@ -50,7 +47,7 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema"> report@bsmd.de hsok@gmx.de bald@puls200.de @@ -65,7 +62,7 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema"> E:\svnlager\BSMD\nsw\Deutschland\BSMD-Formblatt.xlsx @@ -73,12 +70,15 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema"> E:\svnlager\BSMD\nsw\Dänemark\NSW-DK-Excel-Arrival.xlsx E:\svnlager\BSMD\nsw\Dänemark\NSW-DK-Excel-Departure.xlsx + + Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=E:\DATA\DB\NSW.MDF;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False + diff --git a/nsw/Source/bsmd.ExcelReadService/ExcelReadService.cs b/nsw/Source/bsmd.ExcelReadService/ExcelReadService.cs index d1afd932..f128ab2f 100644 --- a/nsw/Source/bsmd.ExcelReadService/ExcelReadService.cs +++ b/nsw/Source/bsmd.ExcelReadService/ExcelReadService.cs @@ -71,6 +71,9 @@ namespace bsmd.ExcelReadService Dictionary reportingPartyDict = DBManager.Instance.GetReportingPartyDict(); + // BSMDPopClient.PrintCapabilities(); + // BSMDPopClient.PrintSslConnectionInfo(); + using (BSMDPopClient bsmdPopClient = new BSMDPopClient()) { if(bsmdPopClient.IsConnected) diff --git a/nsw/Source/bsmd.ExcelReadService/Properties/Settings.Designer.cs b/nsw/Source/bsmd.ExcelReadService/Properties/Settings.Designer.cs index 90689ca9..70417180 100644 --- a/nsw/Source/bsmd.ExcelReadService/Properties/Settings.Designer.cs +++ b/nsw/Source/bsmd.ExcelReadService/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace bsmd.ExcelReadService.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -23,15 +23,6 @@ namespace bsmd.ExcelReadService.Properties { } } - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("replace me!")] - public string ConnectionString { - get { - return ((string)(this["ConnectionString"])); - } - } - [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("300")] @@ -108,5 +99,16 @@ namespace bsmd.ExcelReadService.Properties { return ((global::System.Collections.Specialized.StringCollection)(this["ConfirmationDK"])); } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=E:\\DATA\\DB\\NSW.MDF;Integrated " + + "Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;Appl" + + "icationIntent=ReadWrite;MultiSubnetFailover=False")] + public string ConnectionString { + get { + return ((string)(this["ConnectionString"])); + } + } } } diff --git a/nsw/Source/bsmd.ExcelReadService/Properties/Settings.settings b/nsw/Source/bsmd.ExcelReadService/Properties/Settings.settings index 3e0f92fb..2048bb49 100644 --- a/nsw/Source/bsmd.ExcelReadService/Properties/Settings.settings +++ b/nsw/Source/bsmd.ExcelReadService/Properties/Settings.settings @@ -2,9 +2,6 @@ - - replace me! - 300 @@ -40,5 +37,8 @@ <string>E:\svnlager\BSMD\nsw\Dänemark\NSW-DK-Excel-Departure.xlsx</string> </ArrayOfString> + + Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=E:\DATA\DB\NSW.MDF;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False + \ No newline at end of file diff --git a/nsw/Source/bsmd.database/Properties/AssemblyProductInfo.cs b/nsw/Source/bsmd.database/Properties/AssemblyProductInfo.cs index d46380b9..fea5d68b 100644 --- a/nsw/Source/bsmd.database/Properties/AssemblyProductInfo.cs +++ b/nsw/Source/bsmd.database/Properties/AssemblyProductInfo.cs @@ -2,6 +2,6 @@ [assembly: AssemblyCompany("schick Informatik")] [assembly: AssemblyProduct("BSMD NSW interface")] -[assembly: AssemblyInformationalVersion("6.4.0")] +[assembly: AssemblyInformationalVersion("6.4.1")] [assembly: AssemblyCopyright("Copyright © 2014-2021 schick Informatik")] [assembly: AssemblyTrademark("")] \ No newline at end of file diff --git a/nsw/Source/bsmd.database/Properties/AssemblyProjectInfo.cs b/nsw/Source/bsmd.database/Properties/AssemblyProjectInfo.cs index 49265b3f..0b1870fa 100644 --- a/nsw/Source/bsmd.database/Properties/AssemblyProjectInfo.cs +++ b/nsw/Source/bsmd.database/Properties/AssemblyProjectInfo.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("6.4.0.*")] +[assembly: AssemblyVersion("6.4.1.*")] diff --git a/nsw/Source/bsmd.email/BSMDMail.cs b/nsw/Source/bsmd.email/BSMDMail.cs index 3c0087fe..17d37d87 100644 --- a/nsw/Source/bsmd.email/BSMDMail.cs +++ b/nsw/Source/bsmd.email/BSMDMail.cs @@ -21,8 +21,8 @@ namespace bsmd.email { public class BSMDMail : IDisposable { - private SmtpClient client; - private static ILog log = LogManager.GetLogger(typeof(BSMDMail)); + private readonly SmtpClient client; + private static readonly ILog log = LogManager.GetLogger(typeof(BSMDMail)); internal BSMDMail() { diff --git a/nsw/Source/bsmd.email/BSMDPopClient.cs b/nsw/Source/bsmd.email/BSMDPopClient.cs index b95fb90a..d3f2242d 100644 --- a/nsw/Source/bsmd.email/BSMDPopClient.cs +++ b/nsw/Source/bsmd.email/BSMDPopClient.cs @@ -9,12 +9,19 @@ using System; using System.IO; -using OpenPop.Pop3; -using OpenPop.Mime; using log4net; using System.Security.Cryptography.X509Certificates; using System.Net.Security; +using MimeKit; +using MailKit; +using MailKit.Security; +using MailKit.Net.Pop3; +using MailKit.Net.Imap; +using MailKit.Net.Smtp; + +using EmailValidation; + namespace bsmd.email { public class BSMDPopClient : IDisposable @@ -24,7 +31,7 @@ namespace bsmd.email private readonly Pop3Client pop3Client; private readonly ILog _log = LogManager.GetLogger(typeof(BSMDPopClient)); - private int currentMail = 1; + private int currentMail = 0; #endregion @@ -35,21 +42,15 @@ namespace bsmd.email try { this.pop3Client = new Pop3Client(); - - //bool useSSL = false; // abhängig vom PORT setzen!!! - //if (Properties.Settings.Default.POP3Port != 110) - bool useSSL = true; - - this.pop3Client.Connect(Properties.Settings.Default.POP3Server, Properties.Settings.Default.POP3Port, useSSL, - 60000, 60000, new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCertificate)); - // this.pop3Client.Connect(Properties.Settings.Default.POP3Server, Properties.Settings.Default.POP3Port, useSSL); - + this.pop3Client.ServerCertificateValidationCallback = ValidateServerCertificate; + bool useSSL = true; + this.pop3Client.Connect(Properties.Settings.Default.POP3Server, Properties.Settings.Default.POP3Port, useSSL); this.pop3Client.Authenticate(Properties.Settings.Default.POP3User, Properties.Settings.Default.POP3Password); this.IsConnected = true; } catch (Exception ex) { - _log.ErrorFormat("Error connecting to POP3 Server: {0}", ex.Message); + _log.ErrorFormat("Error connecting to POP3 Server {0}: {1}", Properties.Settings.Default.POP3Server, ex.Message); _log.DebugFormat("User:{0} Pw:{1}", Properties.Settings.Default.POP3User, Properties.Settings.Default.POP3Password); } } @@ -58,8 +59,8 @@ namespace bsmd.email { if (this.pop3Client != null) { - if (this.pop3Client.Connected) - this.pop3Client.Disconnect(); + if (this.pop3Client.IsConnected) + this.pop3Client.Disconnect(true); this.pop3Client.Dispose(); } } @@ -96,19 +97,26 @@ namespace bsmd.email if (!IsConnected) return false; int messageCount = this.pop3Client.GetMessageCount(); - if (messageCount > (this.currentMail - 1)) + if (messageCount > this.currentMail) { - Message mailMessage = this.pop3Client.GetMessage(this.currentMail); + MimeMessage mailMessage = this.pop3Client.GetMessage(this.currentMail); + if((mailMessage.Sender != null) && EmailValidator.Validate(mailMessage.Sender.Address)) + sender = mailMessage.Sender.Address; + if ((sender == null) && mailMessage.From.Count > 0) + { + foreach (MailboxAddress ma in mailMessage.From.Mailboxes) + { + if(EmailValidator.Validate(ma.Address)) + { + sender = ma.Address; + break; + } + } + } - if (mailMessage.Headers.Sender?.HasValidMailAddress == true) - sender = mailMessage.Headers.Sender.MailAddress.Address; - if ((sender == null) && (mailMessage.Headers.From?.HasValidMailAddress == true)) - sender = mailMessage.Headers.From.MailAddress.Address; - - MessagePart aPart = mailMessage.FindFirstPlainTextVersion(); - body = aPart?.GetBodyAsText(); - messageId = mailMessage.Headers.MessageId; - subject = mailMessage.Headers.Subject; + body = mailMessage.TextBody; + messageId = mailMessage.MessageId; + subject = mailMessage.Subject; this.currentMail++; // advance message pointer return true; @@ -129,32 +137,63 @@ namespace bsmd.email if (!IsConnected) return false; int messageCount = this.pop3Client.GetMessageCount(); - if (messageCount > (this.currentMail - 1)) + if (messageCount > this.currentMail) { - Message mailMessage = this.pop3Client.GetMessage(this.currentMail); + MimeMessage mailMessage = this.pop3Client.GetMessage(this.currentMail); - if (mailMessage.Headers.Sender?.HasValidMailAddress == true) - sender = mailMessage.Headers.Sender.MailAddress.Address; - if ((sender == null) && (mailMessage.Headers.From?.HasValidMailAddress == true)) - sender = mailMessage.Headers.From.MailAddress.Address; - - messageId = mailMessage.Headers.MessageId; - subject = mailMessage.Headers.Subject; - - foreach(MessagePart part in mailMessage.FindAllAttachments()) + if((mailMessage.Sender != null) && EmailValidator.Validate(mailMessage.Sender.Address)) + sender = mailMessage.Sender.Address; + if ((sender == null) && (mailMessage.From.Count > 0)) { - _log.InfoFormat("found attachment named {0}, ContentType {1}", part.FileName, part.ContentType); - if (part.FileName.EndsWith(".xls", StringComparison.InvariantCultureIgnoreCase)) + foreach (MailboxAddress ma in mailMessage.From.Mailboxes) { - attachmentLocalFile = Path.Combine(Properties.Settings.Default.ArchiveFolder, part.FileName); - part.Save(new FileInfo(attachmentLocalFile)); + if (EmailValidator.Validate(ma.Address)) + { + sender = ma.Address; + break; + } } - else if (part.FileName.EndsWith(".xlsx", StringComparison.InvariantCultureIgnoreCase)) + } + + messageId = mailMessage.MessageId; + subject = mailMessage.Subject; + + foreach (MimeEntity attachment in mailMessage.Attachments) + { + if (attachment is MessagePart rfc822) { - attachmentLocalFile = Path.Combine(Properties.Settings.Default.ArchiveFolder, part.FileName); - part.Save(new FileInfo(attachmentLocalFile)); + string filename = attachment.ContentDisposition?.FileName; + if(filename == null) + { + _log.InfoFormat("attachment {0} has no filename", attachment.ContentId); + continue; + } + _log.InfoFormat("found attachment named {0}, ContentType {1}", filename, attachment.ContentType); + + if (filename.EndsWith(".xls", StringComparison.InvariantCultureIgnoreCase) || + filename.EndsWith(".xlsx", StringComparison.InvariantCultureIgnoreCase)) + { + attachmentLocalFile = Path.Combine(Properties.Settings.Default.ArchiveFolder, filename); + using (var stream = File.Create(attachmentLocalFile)) + rfc822.Message.WriteTo(stream); + } + } + else + { + var part = (MimePart)attachment; + var filename = part.FileName; + if (filename != null) + { + if (filename.EndsWith(".xls", StringComparison.InvariantCultureIgnoreCase) || + filename.EndsWith(".xlsx", StringComparison.InvariantCultureIgnoreCase)) + { + attachmentLocalFile = Path.Combine(Properties.Settings.Default.ArchiveFolder, filename); + using (var stream = File.Create(attachmentLocalFile)) + part.Content.DecodeTo(stream); + } + } } - } + } this.currentMail++; // advance message pointer @@ -176,15 +215,19 @@ namespace bsmd.email // Run through each of these messages and download the headers for (int messageIndex = messageCount; messageIndex > 0; messageIndex--) { - // If the Message ID of the current message is the same as the parameter given, delete that message - if (this.pop3Client.GetMessageHeaders(messageIndex).MessageId.Equals(messageId)) + // If the Message ID of the current message is the same as the parameter given, delete that message + if (this.pop3Client.GetMessage(messageIndex).MessageId.Equals(messageId)) { // gefunden, Nachricht laden - Message mailMessage = this.pop3Client.GetMessage(messageIndex); - foreach (MessagePart part in mailMessage.FindAllAttachments()) + MimeMessage mailMessage = this.pop3Client.GetMessage(messageIndex); + foreach (MimeEntity attachment in mailMessage.Attachments) { - if (part.FileName.EndsWith(".pdf", StringComparison.InvariantCultureIgnoreCase)) - return part.FileName; + if(attachment is MessagePart) + { + var filename = attachment.ContentDisposition?.FileName; + if ((filename != null) && filename.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) + return attachment.ContentDisposition?.FileName; + } } } } @@ -208,10 +251,10 @@ namespace bsmd.email int messageCount = this.pop3Client.GetMessageCount(); // Run trough each of these messages and download the headers - for (int messageItem = messageCount; messageItem > 0; messageItem--) + for (int messageItem = (messageCount - 1); messageItem >= 0; messageItem--) { // If the Message ID of the current message is the same as the parameter given, delete that message - if (this.pop3Client.GetMessageHeaders(messageItem).MessageId.Equals(messageId)) + if (this.pop3Client.GetMessage(messageItem).MessageId.Equals(messageId)) { // Delete this.pop3Client.DeleteMessage(messageItem); @@ -224,6 +267,87 @@ namespace bsmd.email } #endregion - + + #region MailKit helper / info / logging funcs + + #region SslConnectionInformation + + public static void PrintSslConnectionInfo() + { + using (var client = new SmtpClient()) + { + string host = Properties.Settings.Default.SMTPServer; + client.Connect(host, 587, SecureSocketOptions.Auto); + + Console.WriteLine($"Negotiated the following SSL options with {host}:"); + Console.WriteLine($" Protocol Version: {client.SslProtocol}"); + Console.WriteLine($" Cipher Algorithm: {client.SslCipherAlgorithm}"); + Console.WriteLine($" Cipher Strength: {client.SslCipherStrength}"); + Console.WriteLine($" Hash Algorithm: {client.SslHashAlgorithm}"); + Console.WriteLine($" Hash Strength: {client.SslHashStrength}"); + Console.WriteLine($" Key-Exchange Algorithm: {client.SslKeyExchangeAlgorithm}"); + Console.WriteLine($" Key-Exchange Strength: {client.SslKeyExchangeStrength}"); + + // Example Log: + // + // Negotiated the following SSL options with pop.gmail.com: + // Protocol Version: Tls12 + // Cipher Algorithm: Aes128 + // Cipher Strength: 128 + // Hash Algorithm: Sha256 + // Hash Strength: 0 + // Key-Exchange Algorithm: 44550 + // Key-Exchange Strength: 255 + + client.Disconnect(true); + } + } + + public static void PrintCapabilities() + { + using (var client = new Pop3Client()) + { + client.Connect(Properties.Settings.Default.POP3Server, Properties.Settings.Default.POP3Port, SecureSocketOptions.SslOnConnect); + + if (client.Capabilities.HasFlag(Pop3Capabilities.Sasl)) + { + var mechanisms = string.Join(", ", client.AuthenticationMechanisms); + Console.WriteLine("The POP3 server supports the following SASL mechanisms: {0}", mechanisms); + } + + client.Authenticate(Properties.Settings.Default.POP3User, Properties.Settings.Default.POP3Password); + + if (client.Capabilities.HasFlag(Pop3Capabilities.Apop)) + Console.WriteLine("The server supports APOP authentication."); + + if (client.Capabilities.HasFlag(Pop3Capabilities.Expire)) + { + if (client.ExpirePolicy > 0) + Console.WriteLine("The POP3 server automatically expires messages after {0} days", client.ExpirePolicy); + else + Console.WriteLine("The POP3 server will never expire messages."); + } + + if (client.Capabilities.HasFlag(Pop3Capabilities.LoginDelay)) + Console.WriteLine("The minimum number of seconds between login attempts is {0}.", client.LoginDelay); + + if (client.Capabilities.HasFlag(Pop3Capabilities.Pipelining)) + Console.WriteLine("The POP3 server can pipeline commands, so using client.GetMessages() will be faster."); + + if (client.Capabilities.HasFlag(Pop3Capabilities.Top)) + Console.WriteLine("The POP3 server supports the TOP command, so it's possible to download message headers."); + + if (client.Capabilities.HasFlag(Pop3Capabilities.UIDL)) + Console.WriteLine("The POP3 server supports the UIDL command which means we can track messages by UID."); + + client.Disconnect(true); + } + } + + #endregion + + + #endregion + } } diff --git a/nsw/Source/bsmd.email/bsmd.email.csproj b/nsw/Source/bsmd.email/bsmd.email.csproj index 3b6e88c1..ec0c1d5b 100644 --- a/nsw/Source/bsmd.email/bsmd.email.csproj +++ b/nsw/Source/bsmd.email/bsmd.email.csproj @@ -40,16 +40,29 @@ ..\bsmdKey.snk + + ..\packages\Portable.BouncyCastle.1.8.10\lib\net40\BouncyCastle.Crypto.dll + + + ..\packages\EmailValidation.1.0.4\lib\net45\EmailValidation.dll + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll True - - ..\packages\OpenPop.NET.2.0.6.1120\lib\net40\OpenPop.dll - True + + ..\packages\MailKit.2.13.0\lib\net45\MailKit.dll + + + ..\packages\MimeKit.2.13.0\lib\net45\MimeKit.dll + + ..\packages\System.Buffers.4.5.1\lib\netstandard1.1\System.Buffers.dll + + + diff --git a/nsw/Source/bsmd.email/packages.config b/nsw/Source/bsmd.email/packages.config index 2c7a1f81..c56e467e 100644 --- a/nsw/Source/bsmd.email/packages.config +++ b/nsw/Source/bsmd.email/packages.config @@ -1,5 +1,9 @@  + - + + + + \ No newline at end of file