// // Class: BSMDPopClient // Current CLR: 4.0.30319.34209 // System: Microsoft Visual Studio 10.0 // Author: dani // Created: 6/15/2015 8:43:41 PM // // Copyright (c) 2015 Informatikbüro Daniel Schick. All rights reserved. using System; using System.IO; using log4net; using System.Security.Cryptography.X509Certificates; using System.Net.Security; using MimeKit; using MailKit.Security; using MailKit.Net.Pop3; using MailKit.Net.Smtp; using EmailValidation; namespace bsmd.email { public class BSMDPopClient : IDisposable { #region Fields private readonly Pop3Client pop3Client; private readonly ILog _log = LogManager.GetLogger(typeof(BSMDPopClient)); private int currentMail = 0; #endregion #region Construction / Destruction public BSMDPopClient() { try { this.pop3Client = new Pop3Client(new MailKitLogger()); 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}: {1}", Properties.Settings.Default.POP3Server, ex.Message); _log.DebugFormat("User:{0} Pw:{1}", Properties.Settings.Default.POP3User, Properties.Settings.Default.POP3Password); } } public void Dispose() { if (this.pop3Client != null) { if (this.pop3Client.IsConnected) this.pop3Client.Disconnect(true); this.pop3Client.Dispose(); } } #endregion #region Hack /// /// Diese Funktion validiert *jedes* Zertifikat und ist eigentlich scheiße. Das geht nur so im abgeschlossenen BSMD Netz! /// public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } #endregion #region Properties public bool IsConnected { get; } = false; #endregion #region E-Mail receive helper / convenience funcs public bool ReceiveSingleMailText(out string messageId, out string sender, out string subject, out string body) { messageId = null; sender = null; subject = null; body = null; if (!IsConnected) return false; int messageCount = this.pop3Client.GetMessageCount(); if (messageCount > 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; } } } body = mailMessage.TextBody; messageId = mailMessage.MessageId; subject = mailMessage.Subject; this.currentMail++; // advance message pointer return true; } else { return false; } } public bool ReceiveSingleMail(out string attachmentLocalFile, out string messageId, out string sender, out string subject) { attachmentLocalFile = null; messageId = null; sender = null; subject = null; if (!IsConnected) return false; int messageCount = this.pop3Client.GetMessageCount(); if (messageCount > 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; } } } messageId = mailMessage.MessageId; subject = mailMessage.Subject; foreach (MimeEntity attachment in mailMessage.Attachments) { if (attachment is MessagePart rfc822) { 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 return true; } else { _log.Info("no new mail on server"); return false; } } public string GetNameOfFirstMailEmailPDFAttachment(string messageId) { if (IsConnected) { int messageCount = this.pop3Client.GetMessageCount(); // 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.GetMessage(messageIndex).MessageId.Equals(messageId)) { // gefunden, Nachricht laden MimeMessage mailMessage = this.pop3Client.GetMessage(messageIndex); foreach (MimeEntity attachment in mailMessage.Attachments) { if(attachment is MessagePart) { var filename = attachment.ContentDisposition?.FileName; if ((filename != null) && filename.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) return attachment.ContentDisposition?.FileName; } } } } } return null; } #endregion #region delete designated email /// /// deletes message referenced by messageId /// /// true if successful public bool DeleteMessageByMessageId(string messageId) { if (!IsConnected) return false; // Get the number of messages on the POP3 server int messageCount = this.pop3Client.GetMessageCount(); // Run trough each of these messages and download the headers 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.GetMessage(messageItem).MessageId.Equals(messageId)) { // Delete this.pop3Client.DeleteMessage(messageItem); return true; } } // We did not find any message with the given messageId, report this back return false; } #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 } }