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