// 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
}
}