// Description: IMAP Client analog zu POP3 Client // Copyright (c) 2021 Informatikbüro Daniel Schick using System; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Net.Security; using System.IO; using log4net; using MimeKit; using MailKit; using MailKit.Security; using MailKit.Net.Imap; using EmailValidation; namespace bsmd.email { public class BSMDImapClient : IDisposable { private readonly ImapClient imapClient; private readonly ILog _log = LogManager.GetLogger(typeof(BSMDImapClient)); #region Creation / Destroy public BSMDImapClient() { try { this.imapClient = new ImapClient(new MailKitLogger()); // this.imapClient.ServerCertificateValidationCallback = ValidateServerCertificate; // hack? to accept everything bool useSSL = true; this.imapClient.Connect(Properties.Settings.Default.IMAPServer, Properties.Settings.Default.IMAPPort, useSSL); this.imapClient.Authenticate(Properties.Settings.Default.IMAPUser, Properties.Settings.Default.IMAPPassword); } catch (Exception ex) { _log.ErrorFormat("Error connecting to IMAP Server {0}: {1}", Properties.Settings.Default.IMAPServer, ex.Message); _log.DebugFormat("User:{0} Pw:{1}", Properties.Settings.Default.IMAPUser, Properties.Settings.Default.IMAPPassword); } } public void Dispose() { if (imapClient.IsConnected) imapClient.Disconnect(true); } #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 { return this.imapClient.IsConnected; } } #endregion #region public methods public bool ReceiveSingleMail(out string attachmentLocalFile, out string messageId, out string sender, out string subject) { attachmentLocalFile = null; messageId = null; sender = null; subject = null; if (!imapClient.IsConnected) return false; imapClient.Inbox.Open(FolderAccess.ReadWrite); IList ids = imapClient.Inbox.Search(MailKit.Search.SearchQuery.All); if(ids.Count > 0) { MimeMessage mailMessage = imapClient.Inbox.GetMessage(ids[0]); 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); } } } } return true; // new mail message } _log.Info("no new mail on server"); return false; // no new message on server } /// /// deletes message referenced by messageId /// /// true if successful public bool DeleteMessageByMessageId(string messageId) { if (!IsConnected) return false; // find the message IList deleteList = this.imapClient.Inbox.Search(MailKit.Search.SearchQuery.HeaderContains("Message-Id", messageId)); if(deleteList.Count == 1) { // mark found message as deleted this.imapClient.Inbox.AddFlags(deleteList[0], MessageFlags.Deleted, true); // clear up inbox this.imapClient.Inbox.Expunge(); _log.InfoFormat("Message {0} deleted", messageId); // mark successful return true; } if(deleteList.Count == 0) { _log.WarnFormat("Message {0} to delete not found", messageId); } if(deleteList.Count > 1) { _log.WarnFormat("Very strange: {0} messages found in inbox for id {1}", deleteList.Count, messageId); } return false; } #endregion #region static helper public static void PrintSslConnectionInfo() { using (var client = new ImapClient()) { client.Connect(Properties.Settings.Default.IMAPServer, Properties.Settings.Default.IMAPPort, SecureSocketOptions.Auto); Console.WriteLine($"Negotiated the following SSL options with {Properties.Settings.Default.IMAPServer}:"); 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}"); client.Disconnect(true); } } #endregion } }