352 lines
14 KiB
C#
352 lines
14 KiB
C#
//
|
|
// 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
|
|
|
|
/// <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; } = 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
|
|
|
|
/// <summary>
|
|
/// deletes message referenced by messageId
|
|
/// </summary>
|
|
/// <returns>true if successful</returns>
|
|
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
|
|
|
|
}
|
|
}
|