Files
rockfish/util/RfMail.cs
2018-06-28 23:37:38 +00:00

564 lines
22 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.IO;
using MailKit.Net.Smtp;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit;
using MimeKit;
namespace rockfishCore.Util
{
//http://www.mimekit.net/
public static class RfMail
{
public const string MAIL_SMPT_ADDRESS = "smtp.ayanova.com";
public const int MAIL_SMPT_PORT = 465;
public const string MAIL_IMAP_ADDRESS = "mail.ayanova.com";
public const int MAIL_IMAP_PORT = 993;
public const string MAIL_ACCOUNT_SUPPORT = "support@ayanova.com";
public const string MAIL_ACCOUNT_PASSWORD_SUPPORT = "e527b6c5a00c27bb61ca694b3de0ee178cbe3f1541a772774762ed48e9caf5ce";
public const string MAIL_ACCOUNT_SALES = "sales@ayanova.com";
public const string MAIL_ACCOUNT_PASSWORD_SALES = "6edbae5eb616a975abf86bcd9f45616f5c70c4f05189af60a1caaa62b406149d";
public enum rfMailAccount
{
support = 1,
sales = 2
}
public class rfMailMessage
{
public MimeMessage message;
public uint uid;
}
class DeliverStatusSmtpClient : SmtpClient
{
protected override DeliveryStatusNotification? GetDeliveryStatusNotifications(MimeMessage message, MailboxAddress mailbox)
{
return DeliveryStatusNotification.Delay |
DeliveryStatusNotification.Failure |
DeliveryStatusNotification.Success;
}
}
/////////////////////////////////////////////////////////
//
// Do the sending with optional deliver status receipt
//
public static void DoSend(MimeMessage message, string MailAccount, string MailAccountPassword, bool trackDeliveryStatus)
{
if (trackDeliveryStatus)
{
//set the return receipt and disposition to headers
message.Headers.Add("Return-Receipt-To", "<" + MailAccount + ">");
message.Headers.Add("Disposition-Notification-To", "<" + MailAccount + ">");
using (var client = new DeliverStatusSmtpClient())
{
//Accept all SSL certificates (in case the server supports STARTTLS)
//(we have a funky cert on the mail server)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
// Note: only needed if the SMTP server requires authentication
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
client.Send(message);
client.Disconnect(true);
}
}
else
{
using (var client = new SmtpClient())
{
//Accept all SSL certificates (in case the server supports STARTTLS)
//(we have a funky cert on the mail server)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
// Note: only needed if the SMTP server requires authentication
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
client.Send(message);
client.Disconnect(true);
}
}
}
public static void SendMessage(string MessageFrom, string MessageTo, string MessageSubject, string MessageBody,
bool trackDeliveryStatus = false, string CustomHeader = "", string CustomHeaderValue = "")
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(MessageFrom));
message.To.Add(new MailboxAddress(MessageTo));
message.Subject = MessageSubject;
message.Body = new TextPart("plain")
{
Text = MessageBody
};
message.Headers["X-Mailer"] = RfVersion.Full;
if (CustomHeader != "" && CustomHeaderValue != "")
{
message.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
}
//send with optional tracking
DoSend(message, MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT, trackDeliveryStatus);
// using (var client = new SmtpClient())
// {
// //Accept all SSL certificates (in case the server supports STARTTLS)
// //(we have a funky cert on the mail server)
// client.ServerCertificateValidationCallback = (s, c, h, e) => true;
// client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT, true);
// // Note: since we don't have an OAuth2 token, disable
// // the XOAUTH2 authentication mechanism.
// client.AuthenticationMechanisms.Remove("XOAUTH2");
// // Note: only needed if the SMTP server requires authentication
// client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
// client.Send(message);
// client.Disconnect(true);
// }
}//send message
public static void ReplyMessage(uint replyToMessageId, rfMailAccount replyFromAccount, string replyBody,
bool replyToAll, bool trackDeliveryStatus = false, string CustomHeader = "", string CustomHeaderValue = "")
{
//get the original to reply to it:
MimeMessage message = GetMessage(replyToMessageId, replyFromAccount);
if (message == null)
{
throw new System.ArgumentException("RfMail:ReplyMessage->source message not found (id=" + replyToMessageId.ToString() + ")");
}
//construct the new message
var reply = new MimeMessage();
MailboxAddress from = null;
string from_account = "";
string from_account_password = "";
switch (replyFromAccount)
{
case rfMailAccount.sales:
from_account = MAIL_ACCOUNT_SALES;
from_account_password = MAIL_ACCOUNT_PASSWORD_SALES;
from = new MailboxAddress(from_account);
break;
default:
from_account = MAIL_ACCOUNT_SUPPORT;
from_account_password = MAIL_ACCOUNT_PASSWORD_SUPPORT;
from = new MailboxAddress(from_account);
break;
}
reply.From.Add(from);
// reply to the sender of the message
if (message.ReplyTo.Count > 0)
{
reply.To.AddRange(message.ReplyTo);
}
else if (message.From.Count > 0)
{
reply.To.AddRange(message.From);
}
else if (message.Sender != null)
{
reply.To.Add(message.Sender);
}
if (replyToAll)
{
// include all of the other original recipients (removing ourselves from the list)
reply.To.AddRange(message.To.Mailboxes.Where(x => x.Address != from.Address));
reply.Cc.AddRange(message.Cc.Mailboxes.Where(x => x.Address != from.Address));
}
// set the reply subject
if (!message.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase))
reply.Subject = "Re: " + message.Subject;
else
reply.Subject = message.Subject;
// construct the In-Reply-To and References headers
if (!string.IsNullOrEmpty(message.MessageId))
{
reply.InReplyTo = message.MessageId;
foreach (var id in message.References)
reply.References.Add(id);
reply.References.Add(message.MessageId);
}
// quote the original message text
using (var quoted = new StringWriter())
{
var sender = message.Sender ?? message.From.Mailboxes.FirstOrDefault();
var name = sender != null ? (!string.IsNullOrEmpty(sender.Name) ? sender.Name : sender.Address) : "someone";
quoted.WriteLine("On {0}, {1} wrote:", message.Date.ToString("f"), name);
using (var reader = new StringReader(message.TextBody))
{
string line;
while ((line = reader.ReadLine()) != null)
{
quoted.Write("> ");
quoted.WriteLine(line);
}
}
reply.Body = new TextPart("plain")
{
Text = replyBody + "\r\n\r\n\r\n" + quoted.ToString()
};
}
reply.Headers["X-Mailer"] = RfVersion.Full;
if (CustomHeader != "" && CustomHeaderValue != "")
{
reply.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
}
//send with optional tracking
DoSend(reply, from_account, from_account_password, trackDeliveryStatus);
// using (var client = new SmtpClient())
// {
// //Accept all SSL certificates (in case the server supports STARTTLS)
// //(we have a funky cert on the mail server)
// client.ServerCertificateValidationCallback = (s, c, h, e) => true;
// client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT, true);
// // Note: since we don't have an OAuth2 token, disable
// // the XOAUTH2 authentication mechanism.
// client.AuthenticationMechanisms.Remove("XOAUTH2");
// // Note: only needed if the SMTP server requires authentication
// client.Authenticate(from_account, from_account_password);
// client.Send(reply);
// client.Disconnect(true);
// }
//flag the message as having been replied to
FlagInboxMessageSeenReplied(replyToMessageId, replyFromAccount);
}//send message
//Fetch message by UID
public static MimeMessage GetMessage(uint uid, rfMailAccount fromAccount = rfMailAccount.support)
{
using (var client = new ImapClient())
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
if (fromAccount == rfMailAccount.support)
{
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
}
else
{
client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
}
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
var m = inbox.GetMessage(new UniqueId(uid));
client.Disconnect(true);
return m;
}
}//get message
//Fetch messages by Search query
public static List<rfMailMessage> GetMessages(SearchQuery query)
{
List<rfMailMessage> ret = new List<rfMailMessage>();
using (var client = new ImapClient())
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
foreach (var uid in inbox.Search(query))
{
ret.Add(new rfMailMessage { message = inbox.GetMessage(uid), uid = uid.Id });
}
client.Disconnect(true);
}
return ret;
}//get message
//Flag message as seen and replied by UID
public static bool FlagInboxMessageSeenReplied(uint uid, rfMailAccount inAccount = rfMailAccount.support)
{
using (var client = new ImapClient())
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
if (inAccount == rfMailAccount.support)
{
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
}
else
{
client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
}
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadWrite);
inbox.AddFlags(new UniqueId(uid), MessageFlags.Seen, true);
inbox.AddFlags(new UniqueId(uid), MessageFlags.Answered, true);
client.Disconnect(true);
return true;
}
}//get message
////////////////////////////////////////////////////
//Put a message in the drafts folder of support
//
public static void DraftMessage(string MessageFrom, string MessageTo, string MessageSubject, string MessageBody,
string CustomHeader = "", string CustomHeaderValue = "")
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(MessageFrom));
//case 3512 handle more than one email in the address
if (MessageTo.Contains(","))
{
List<MailboxAddress> mbAll = new List<MailboxAddress>();
var addrs = MessageTo.Split(',');
foreach (string addr in addrs)
{
mbAll.Add(new MailboxAddress(addr.Trim()));
}
message.To.AddRange(mbAll);
}
else
{
message.To.Add(new MailboxAddress(MessageTo));
}
message.Subject = MessageSubject;
message.Body = new TextPart("plain")
{
Text = MessageBody
};
if (CustomHeader != "" && CustomHeaderValue != "")
{
message.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
}
//adapted from https://stackoverflow.com/questions/33365072/mailkit-sending-drafts
using (var client = new ImapClient())
{
try
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
var draftFolder = client.GetFolder("Drafts");//Our surgemail server works with this other servers in future might not
if (draftFolder != null)
{
draftFolder.Open(FolderAccess.ReadWrite);
draftFolder.Append(message, MessageFlags.Draft);
//draftFolder.Expunge();
}
}
catch (Exception ex)
{
throw new System.Exception("RfMail->DraftMessage() - Exception has occured: " + ex.Message);
}
client.Disconnect(true);
}
}//draft message
/////////////////////////////////////////////////////////////////
//Fetch summaries of unread messages in sales and support
//inboxes
//
public static List<rfMessageSummary> GetSalesAndSupportSummaries()
{
List<rfMessageSummary> ret = new List<rfMessageSummary>();
ret.AddRange(getInboxSummariesFor(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT));
ret.AddRange(getInboxSummariesFor(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES));
return ret;
}
private static List<rfMessageSummary> getInboxSummariesFor(string sourceAccount, string sourcePassword)
{
List<rfMessageSummary> ret = new List<rfMessageSummary>();
using (var client = new ImapClient())
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(sourceAccount, sourcePassword);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
var summaries = inbox.Fetch(0, -1, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
client.Disconnect(true);
foreach (var summary in summaries)
{
//Sometimes bad hombres don't set a from address so don't expect one
string sFrom = "UNKNOWN / NOT SET";
if (summary.Envelope.From.Count > 0)
{
sFrom = summary.Envelope.From[0].ToString().Replace("\"", "").Replace("<", "").Replace(">", "").Trim();
}
ret.Add(new rfMessageSummary
{
account = sourceAccount,
id = summary.UniqueId.Id,
subject = summary.Envelope.Subject,
sent = DateUtil.DateTimeOffSetNullableToEpoch(summary.Envelope.Date),
from = sFrom,
flags = summary.Flags.ToString().ToLowerInvariant()
});
}
}
//reverse the results array as emails come in oldest first order but we want oldest last
ret.Reverse();
return ret;
}
public class rfMessageSummary
{
public string account;
public uint id;
public string from;
public string subject;
public long? sent;
public string flags;
}
//Fetch a single string preview of message by Account / folder / UID
public static rfMessagePreview GetMessagePreview(string mailAccount, string mailFolder, uint uid)
{
using (var client = new ImapClient())
{
// Accept all SSL certificates
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
//TODO: make accounts reside in dictionary in future
if (mailAccount == "support@ayanova.com")
{
client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
}
if (mailAccount == "sales@ayanova.com")
{
client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
}
var fldr = client.GetFolder(mailFolder);
fldr.Open(FolderAccess.ReadOnly);
var m = fldr.GetMessage(new UniqueId(uid));
client.Disconnect(true);
StringBuilder sb = new StringBuilder();
sb.Append("From: ");
sb.AppendLine(m.From[0].ToString().Replace("\"", "").Replace("<", "").Replace(">", "").Trim());
sb.Append("To: ");
sb.AppendLine(mailAccount);
sb.Append("Subject: ");
sb.AppendLine(m.Subject);
sb.AppendLine();
sb.AppendLine();
sb.AppendLine(m.GetTextBody(MimeKit.Text.TextFormat.Plain));
rfMessagePreview preview = new rfMessagePreview();
preview.id = uid;
preview.preview = sb.ToString();
preview.isKeyRequest = m.Subject.StartsWith("Request for 30 day temporary");
return preview;
}
}//get message
public class rfMessagePreview
{
public bool isKeyRequest;
public string preview;
public uint id;
}
//----------------
}//eoc
}//eons