How to extract body from email - c#

Here is my code I am trying to get body of the email from textdata property but it is giving error object reference not set to instance of an object I dont have a clue what to do
IMAPConfig config = new IMAPConfig("imap-mail.outlook.com","name#hotmail.com", "password", true, true, "");
config.CacheFile = "";
IMAPClient client = null;
client = new IMAPClient(config, null, 5);
IMAPFolder f = client.Folders["Inbox"];
// Console.WriteLine(f.GetMessageByID(7049)) ;
int[] msgCount = null;
while (msgCount == null || msgCount.Length == 0)
{
msgCount = f.CheckForNewMessages();
Thread.Sleep(1000);
}
foreach (int id in msgCount)
{
IMAPMessage msg = f.GetMessageByID(id);
string a = null;
a = msg.TextData.TextData;
//MessageBox.Show(msg.TextData.ToString());
msg.MarkAsRead();
}

Seeing as how InterIMAP is a dead project, I would highly recommend switching to MailKit which is not only actively maintained (by me), but also has a lot more features (I mean, InterIMAP's home page says it will get support for copying messages soon and hasn't been updated since 2009), has a lot more users, and a lot fewer bugs. It's also a lot easier to use.
For example, here's how you would do what you are trying to do with MailKit:
using (var client = new ImapClient ()) {
client.Connect ("imap-mail.outlook.com", 993, true);
client.Authenticate ("name#hotmail.com", "password");
client.Inbox.Open (FolderAccess.ReadWrite);
for (int i = 0; i < client.Inbox.Count; i++) {
var message = client.Inbox.GetMessage (i);
var html = message.HtmlBody;
var text = message.TextBody;
// mark the message as read
client.Inbox.AddFlags (id, MessageFlags.Seen, true);
}
client.Disconnect (true);
}
I've also got MSDN-style documentation (that I'm always working on improving) at http://www.mimekit.net/docs that you might find helpful. If you have any questions, my email address can be found on the github project page (the "MailKit" link at the top of my answer).

It looks like you are using InterIMAP for this. If you look at the source code for IMAPMessage.TextData (https://interimap.codeplex.com/SourceControl/latest#InterIMAP/InterIMAP/Objects/IMAPMessage.cs) you can see that it will return null if there is no plain text body part in the message.
/// <summary>
/// The content of the message as plain text (if available)
/// </summary>
[XmlIgnore]
public IMAPMessageContent TextData
{
get
{
//RefreshData(true, false);
foreach (IMAPMessageContent content in _bodyParts)
{
if (content.ContentType.ToLower().Contains("plain"))
return content;
}
return null;
}
//set { _textData = value; }
}

Related

How to vertically format embedded fields

Current Formatting For Embed Fields
Here is an embed I currently use for my semi-public Ark Servers.
First field is the Map name,
Second field is the direct connect IP Address,
Third field is if/where there is a community base on that map.
As you can see it works as intended but if there's to much info on a single line in the field the formatting is screwed up. Is there a way to fix this?
I'm using 3 separate stream builders to build the different fields and then adding them to the embed. If code is needed I can post a "dumbed down version" so it doesn't take up the whole page.
var linkHeading = "steam://connect/";
var sb = new StringBuilder();
var sb2 = new StringBuilder();
var sb3 = new StringBuilder();
var embed = new EmbedBuilder();
embed.WithColor(new Color(0, 255, 0));
embed.Title = "List of Server Ips";
JObject o1;
using (StreamReader file = File.OpenText("serverips.json"))
using (JsonTextReader reader = new JsonTextReader(file))
{
o1 = (JObject)JToken.ReadFrom(reader);
}
var ipsObject = JsonConvert.DeserializeObject<Rootobject>(o1.ToString());
sb.AppendLine("The Island: ");
sb2.AppendLine($"{linkHeading}{ipsObject.TheIsland.ip}:{ipsObject.TheIsland.port}/");
if(ipsObject.TheIsland.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.TheIsland.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
sb.AppendLine("Aberration: ");
sb2.AppendLine($"{linkHeading}{ipsObject.Aberration.ip}:{ipsObject.Aberration.port}/");
if (ipsObject.Aberration.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.Aberration.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
embed.WithDescription($"Cluster Ip and Comm Information");
embed.AddField(x =>
{
x.Name = "Map";
x.Value = sb.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "IP";
x.Value = sb2.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "Comm?";
x.Value = sb3.ToString();
x.IsInline = true;
});
await Context.User.SendMessageAsync(null, false, embed.Build());
await ReplyAsync("Server Ip List was sent directly to your inbox! :)");
You don't have that much control over how embed fields are displayed. The only thing you control in regards to fields are if they inline or not. The rendering is completely up to Discord and the end users screen size. For example, your current output on mobile will ignore the inline setting and list the fields one on top of the other instead of side by side.
Unless your fields consistently contain a small amount of text each you can't guarantee how the end use will see the output. If you need to guarantee some sort of consistent structured display across all devices, your best bet is to use an image.

get the undeliverable email address with mimekit/mailkit library

I started using #jstedfast Mimekit/Mailkit library, which is great by the way, to pull undeliverables using its subject line for search. I tried using the message.to, message.resentto.
how do i get that information. My first try today. I was able to get the list and the body but I just need the email. I tried using s22.imap but there's no support anymore then I discovered this. I know you're active here #jstedfast I need you help..there's no discussion tab in your github.
Thanks in advance
If you look at the raw message source, does the value of the Content-Type header match something along the lines of multipart/report; report-type=delivery-status? If so, it is very likely that this message will have a sub-part with a Content-Type header with a value of message/delivery-status. It should be the second child part of the multipart/report (the first part should be a human-readable explanation).
If so, you can cast the message/delivery-status MimeEntity to an instance of a MessageDeliveryStatus. You'll notice that the MessageDeliveryStatus class has a property called StatusGroups which is a list of multiple collections of headers (e.g. a list of HeaderList objects).
Each HeaderList contains information about a particular recipient that failed. I think what you want to do is look at the Final-Recipient header which will contain 2 pieces of information:
The address-type (should typically be "rfc822")
The mailbox of the recipient (which is what you want)
Unfortunately, MimeKit does not have any tools to parse the Final-Recipient header, but it should be trivial to locate the end of the address-type parameter in the header value by using IndexOf (';') and then you can use something like MailboxAddress.TryParse() to parse it (or you could probably just Substring() the value w/o parsing).
So, the way this might look in code is this:
string[] GetUndeliverableAddresses (MimeMessage message)
{
var report = message.Body as MultipartReport;
if (report == null)
throw new ArgumentException ("This is not a multipart/report!");
MessageDeliveryStatus status = null;
for (int i = 0; i < report.Count; i++) {
status = report[i] as MessageDeliveryStatus;
if (status != null)
break;
}
if (status == null)
throw new ArgumentException ("Did not contain a message/delivery-status!");
var undeliverables = new List<string> ();
for (int i = 0; i < status.StatusGroups.Count; i++) {
var recipient = status.StatusGroups[i]["Final-Recipient"];
int semicolon = recipient.IndexOf (';');
var undeliverable = recipient.Substring (semicolon + 1).Trim ();
undeliverables.Add (undeliverable);
}
return undeliverables.ToArray ();
}
For more information on message/delivery-status, see https://www.rfc-editor.org/rfc/rfc3464
Hope that helps.
this is what I did
foreach (var uid in inbox.Search(query))
{
var message = inbox.GetMessage(uid);
// Console.WriteLine("Subject: {0}", message.Subject);
//Console.WriteLine("Subject: {0}", message.Headers);
// Console.WriteLine("Subject: {0}", message.BodyParts);
var text = message.BodyParts;
string src = text.ElementAt(1).ToString();
int srcStart = src.IndexOf("RFC822;",0); << i used final-recipient intially
int srcEnd = src.IndexOf("Action", 0);
Console.WriteLine("Email:" + src.Substring(srcStart + 8, srcEnd - (srcStart + 8)));
Console.WriteLine(src);
//foreach (var part in message.BodyParts)
//{
// Console.WriteLine(part);
// // do something
//}
}
let me know if there could be a problem..will I get the rfc822 if the recipient inbox is full? there's no way i can test that.. I tested with emails with nonexistent domain, mailbox does not exist..with live.com, randomdomain.com,yahoo.com and gmail. gmail, on the other hand does not return any undeliverables.

how to distinguish between inline image and signature and other blank images in email Imap

I'm using Mailkit to fetch email from mailbox and save it to database to display in my MVC application.
I save html email as plain text in database, i can fetch attachments and save it in file system, but when there are inline images in email, i'm having issue as signatures and other blank images are too being saved as attachment in file system.
Is there a way to distinguish between inline attachment and signatures or other blank images?
Thanks in advance
It doesn't matter which IMAP library you use, none of them have a feature that will help you do what you want to do because it's a non-trivial problem to solve that you are going to need to use some ingenuity to solve.
What you can do is start with the HtmlPreviewVisitor sample from the FAQ and modify it every-so-slightly to just split the attachments into 2 lists:
The list of actual attachments
The list of images actually referenced by the HTML (by walking the HTML and tracking which images are referenced)
code:
/// <summary>
/// Visits a MimeMessage and splits attachments into those that are
/// referenced by the HTML body vs regular attachments.
/// </summary>
class AttachmentVisitor : MimeVisitor
{
List<MultipartRelated> stack = new List<MultipartRelated> ();
List<MimeEntity> attachments = new List<MimeEntity> ();
List<MimePart> embedded = new List<MimePart> ();
bool foundBody;
/// <summary>
/// Creates a new AttachmentVisitor.
/// </summary>
public AttachmentVisitor ()
{
}
/// <summary>
/// The list of attachments that were in the MimeMessage.
/// </summary>
public IList<MimeEntity> Attachments {
get { return attachments; }
}
/// <summary>
/// The list of embedded images that were in the MimeMessage.
/// </summary>
public IList<MimePart> EmbeddedImages {
get { return embedded; }
}
protected override void VisitMultipartAlternative (MultipartAlternative alternative)
{
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
for (int i = alternative.Count - 1; i >= 0 && !foundBody; i--)
alternative[i].Accept (this);
}
protected override void VisitMultipartRelated (MultipartRelated related)
{
var root = related.Root;
// push this multipart/related onto our stack
stack.Add (related);
// visit the root document
root.Accept (this);
// pop this multipart/related off our stack
stack.RemoveAt (stack.Count - 1);
}
// look up the image based on the img src url within our multipart/related stack
bool TryGetImage (string url, out MimePart image)
{
UriKind kind;
int index;
Uri uri;
if (Uri.IsWellFormedUriString (url, UriKind.Absolute))
kind = UriKind.Absolute;
else if (Uri.IsWellFormedUriString (url, UriKind.Relative))
kind = UriKind.Relative;
else
kind = UriKind.RelativeOrAbsolute;
try {
uri = new Uri (url, kind);
} catch {
image = null;
return false;
}
for (int i = stack.Count - 1; i >= 0; i--) {
if ((index = stack[i].IndexOf (uri)) == -1)
continue;
image = stack[i][index] as MimePart;
return image != null;
}
image = null;
return false;
}
// called when an HTML tag is encountered
void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter)
{
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) {
// search for the src= attribute
foreach (var attribute in ctx.Attributes) {
if (attribute.Id == HtmlAttributeId.Src) {
MimePart image;
if (!TryGetImage (attribute.Value, out image))
continue;
if (!embedded.Contains (image))
embedded.Add (image);
}
}
}
}
protected override void VisitTextPart (TextPart entity)
{
TextConverter converter;
if (foundBody) {
// since we've already found the body, treat this as an
// attachment
attachments.Add (entity);
return;
}
if (entity.IsHtml) {
converter = new HtmlToHtml {
HtmlTagCallback = HtmlTagCallback
};
converter.Convert (entity.Text);
}
foundBody = true;
}
protected override void VisitTnefPart (TnefPart entity)
{
// extract any attachments in the MS-TNEF part
attachments.AddRange (entity.ExtractAttachments ());
}
protected override void VisitMessagePart (MessagePart entity)
{
// treat message/rfc822 parts as attachments
attachments.Add (entity);
}
protected override void VisitMimePart (MimePart entity)
{
// realistically, if we've gotten this far, then we can treat
// this as an attachment even if the IsAttachment property is
// false.
attachments.Add (entity);
}
}
To use it:
var visitor = new AttachmentVisitor ();
message.Accept (visitor);
// Now you can use visitor.Attachments and visitor.EmbeddedImages
An even simpler, although less error-proof (sine it doesn't actually verify whether the image is referenced by the HTML), way of doing it is this:
var embeddedImages = message.BodyParts.OfType<MimePart> ().
Where (x => x.ContentType.IsMimeType ("image", "*") &&
x.ContentDisposition != null &&
x.ContentDisposition.Disposition.Equals ("inline" StringComparison.OrdinalIgnoreCase));
Now that you have your list of embeddedImages, you'll have to figure out a way to determine if they are only used in the signature or used elsewhere in the HTML.
Most likely you'll have to analyze the HTML itself as well.
It is also probably worth noting that some HTML mail will reference images located on the web that are not embedded in the MIME of the message. If you want these images as well, you'll need to modify TryGetImage to fall back to downloading the image from the web if the code I provided fails to locate it within the MIME of the message.
For text/plain messages (which can't use images at all), the common convention to separate the signature from the rest of the message body is a line with only 2 dashes and a space: --.
From my limited experience with HTML messages that have signatures, they do not appear to follow a similar convention. Looking at a few of the HTML messages I receive from co-workers at Microsoft using Outlook, they appear to be within a <table> at the end of the message. However, this assumes that the message is not a reply. Once you start parsing message replies, this <table> ends up in the middle of the message somewhere because the original message being replied to is at the end.
Since everyone's signature is different as well, I'm not sure if this <table> similarity is an Outlook convention or if people are manually constructing their signatures and they are all just using tables out of coincidence (I've also only seen a few, most do not use signatures, so my sample size is very small).
Using https://mailsystem.codeplex.com/:
the class wich read the email:
class readMail:IDisposable
{
public Imap4Client client = new Imap4Client();
public readMail(string mailServer, int port, bool ssl, string login, string password)
{
Pop3Client pop = new Pop3Client();
if (ssl)
{
client.ConnectSsl(mailServer, port);
}
else
client.Connect(mailServer, port);
client.Login(login, password);
}
public IEnumerable<Message> GetAllMails(string mailBox)
{
IEnumerable<Message> ms = GetMails(mailBox, "ALL").Cast<Message>();
return GetMails(mailBox, "ALL").Cast<Message>();
}
protected Imap4Client Client
{
get { return client ?? (client = new Imap4Client()); }
}
private MessageCollection GetMails(string mailBox, string searchPhrase)
{
try
{
MessageCollection messages = new MessageCollection();
Mailbox mails = new Mailbox();
mails = Client.SelectMailbox(mailBox);
messages = mails.SearchParse(searchPhrase);
return messages;
}
catch(Exception ecc)
{
}
}
public void Dispose()
{
throw new NotImplementedException();
}
}
and then:
using (readMail read = new readMail("host.name.information", port, true, username, password) )
{
var emailList = read.GetAllMails(this.folderEmail);
int k = 0;
Mailbox bbb = read.client.SelectMailbox(this.folderEmail);
int[] unseen = bbb.Search("UNSEEN");
foreach (Message email in emailList)
{
/// Contains all parts for which no Content-Disposition header was found. Disposition is left to the final agent.
MimePartCollection im1= email.UnknownDispositionMimeParts;
//Collection containing embedded MIME parts of the message (included text parts)
EmbeddedObjectCollection im2 = email.EmbeddedObjects;
//Collection containing attachments of the message.
AttachmentCollection attach=email.Attachments;
}
}
in my case all the signature's images were in UnknownDispositionMimeParts, but this could be a specific case (different email client and so on)..so for what i know i didn't find any library that separate embedded images from contextual images to signature images

Open new mail interaction window in Genesys Interaction Workspace

I got the task to show the "new outbound mail" dialog in Genesys IWS upon an external event from a webservice. I put my IWS extension in place and it loads and can provide a webservice interface.
My main problem now is that I don't understand how I can open the interactions window from my code. I tried to get an instance of it by using:
IInteractionsWindow interactionsView = Container.Resolve<IInteractionsWindow>();
interactionsView.Create();
interactionsView.ShowView();
This actually works only halfway, as I get a new window, but it's completely empty. Do I need to load every single region on its own? Is there a simpler way to achieve my goals in a fully integrated way?
UPDATE: I have now tried to achieve things using the Platform SDK although I have no idea if this really helps me in showing the "new mail" window to the agent. I tried with the following code:
interactionServerProtocol = new InteractionServerProtocol(new Endpoint(new Uri("tcp://ixnServer:7319")));
interactionServerProtocol.ClientName = "CRMIntegrationModule";
interactionServerProtocol.ClientType = InteractionClient.AgentApplication;
contactServerProtocol = new UniversalContactServerProtocol(new Endpoint(new Uri("tcp://ucsServer:5130")));
contactServerProtocol.ClientName = "CRMIntegrationModule";
interactionServerProtocol.Open();
contactServerProtocol.Open();
RequestSubmit request = RequestSubmit.Create();
request.InteractionType = "Outbound";
request.InteractionSubtype = "OutboundNew";
request.MediaType = "email";
request.Queue = "default";
EventAck response = interactionServerProtocol.Request(request) as EventAck;
if (response != null)
{
string id = Convert.ToString(response.Extension["InteractionId"]);
RequestInsertInteraction insertRequest = RequestInsertInteraction.Create();
insertRequest.InteractionAttributes = new InteractionAttributes
{
Id = id,
MediaTypeId = "email",
TypeId = "Outbound",
SubtypeId = "OutboundNew",
TenantId = 101,
Status = new NullableStatuses(Statuses.Pending),
Subject = "Testmail",
EntityTypeId = new NullableEntityTypes(EntityTypes.EmailOut)
};
insertRequest.EntityAttributes = new EmailOutEntityAttributes()
{
FromAddress = "dummy#gmx.net",
ToAddresses = "dummy#gmx.net",
};
insertRequest.InteractionContent = new InteractionContent()
{
Text = "This is the e-mail body."
};
contactServerProtocol.Send(insertRequest);
RequestPlaceInQueue queueRequest = RequestPlaceInQueue.Create();
queueRequest.InteractionId = id;
queueRequest.Queue = "default";
interactionServerProtocol.Send(queueRequest);
}
interactionServerProtocol.Close();
contactServerProtocol.Close();
The bad thing is the response from the interaction server which is:
attr_ref_id [int] = 2
attr_error_code [int] = 34
attr_error_desc [str] = "Client is not logged in"
I think this could be related to not being logged in correctly somehow but I have not a single clue how to achieve this. Any help?
UPDATE 2 I could send an e-mail using the Platform SDK, but this is not what I really want. The initial question is still valid, as I just want to invoke the interactions window and that's it. The other stuff is up to the user. Is it possible?
You need to use PlatformSDK. add Genesyslab.platform.webmedia.protocols.dll
After that you can use *webmedia.tserver.request, under that tab there is requestWeb or sth.
channelService.RegisterEvents(tServerChannel, new Action<Genesyslab.Enterprise.Model.Channel.IClientChannel>
In your main module(have Initialize method), need to registerevent like that. You can put a button or sth, then you can trigger event or you can use commandchain after logon, is up to you.
Good luck.
I made use of the given command chains:
public IObjectContainer Container { get; set; }
public void NewItem(string contactId, string emailAddress)
{
IAgent agent = Container.Resolve<IAgent>();
IRoutingBasedManager routingManager = Container.Resolve<IRoutingBasedManager>();
IDictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("CommandParameter", agent.FirstMediaEmail);
parameters.Add("TargetId", contactId);
parameters.Add("OwnerId", agent.ConfPerson.EmailAddress);
parameters.Add("Destination", emailAddress);
parameters.Add("RecentIndex", contactId);
bool todo = routingManager.RequestToDo("CreateNewOutboundEmail", RoutingBasedTarget.Contact, parameters);
if (todo && parameters.ContainsKey("RoutingBaseCommand"))
{
IChainOfCommand chainOfCommand = parameters["RoutingBaseCommand"] as IChainOfCommand;
if (chainOfCommand != null)
{
chainOfCommand.Execute(parameters["RoutingBaseCommandParameters"]);
}
}
}

EWS managed API. IsNew Always return false and can't use TextBody

I am a newbie developer and I have been stuck with EWS for hours now. I need to read through the most recent emails, get all the unread emails and use the data from them to do some stuff.
At this moment My code looks like this.
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.Credentials = new WebCredentials("support#mycompany.com", "mysupersuperdupersecretpassword");
service.AutodiscoverUrl("support#mycompany.com", RedirectionUrlValidationCallback);
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox,new ItemView(2));
foreach (Item item in findResults.Items)
{
// works perfectly until here
Console.WriteLine(item.Subject);
Console.WriteLine('\n');
item.Load();
string temp = item.Body.Text;
// I can't seem to get TextBody to work. so I used a RegEx Match match = Regex.Match(temp, "<body>.*?</body>", RegexOptions.Singleline);
string result = match.Value;
result = result.Replace("<body>", "");
result = result.Replace("</body>", "");
Console.Write(result);
Console.WriteLine('\n');
//Now the email boddy is fine but IsNew always returns false.
if (item.IsNew)
{
Console.WriteLine("This message is unread!");
}
else
{
Console.WriteLine("This message is read!");
}
}
}
I have googled and tried and googled some more and I am stuck. How do I now which emails are read, is there a way to get the email body text that's more effective than what I have done ? Any help would be super appreciated.
The MSDN article for usage is pretty good if you haven't already read it.
For your issue, cast your item to an EmailMessage
foreach (Item item in findResults.Items)
{
var mailItem = (EmailMessage)item;
// works perfectly until here
Console.WriteLine(mailItem.Subject);
}
I did notice you are not using Property Sets, but having only used EWS for event notifications and not going through existing mails, it may be different.
UPDATE Additions in light of your changes
use this for your Property Set
new PropertySet(BasePropertySet.FirstClassProperties) {
RequestedBodyType = BodyType.Text
};
Also this reads a little nicer and uses the Body.Text property
foreach (Item myItem in findResults.Items.Where(i=>i is EmailMessage))
{
var mailItem = myItem as EmailMessage;
Console.WriteLine(mailItem.Subject);
mailItem.Load(new PropertySet(BasePropertySet.FirstClassProperties) {
RequestedBodyType = BodyType.Text
}); // Adding this parameter does the trick :)
Console.WriteLine(mailItem.Body.Text);
if(! mailItem.IsRead)
Console.WriteLine("Who is Your Daddy!!!!");
}

Categories

Resources