215 lines
8.3 KiB
C#
215 lines
8.3 KiB
C#
// 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
|
|
|
|
/// <summary>
|
|
/// Diese Funktion validiert *jedes* Zertifikat und ist eigentlich scheiße. Das geht nur so im abgeschlossenen BSMD Netz!
|
|
/// </summary>
|
|
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<UniqueId> 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
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// deletes message referenced by messageId
|
|
/// </summary>
|
|
/// <returns>true if successful</returns>
|
|
public bool DeleteMessageByMessageId(string messageId)
|
|
{
|
|
if (!IsConnected) return false;
|
|
|
|
// find the message
|
|
IList<UniqueId> 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
|
|
|
|
}
|
|
}
|