I'm trying to obtain some information from emails sent to an Outlook email. I've successfully connected to the Exchange Server and have been able to retrieve some information from emails with attachments (I am skipping emails without attachments).
What I Have: I can retrieve the attachment file name, the email date, and the email subject.
What I Need: I need to retrieve the sender name and email also. From what I've done, I can retreive the body of the email (in HTML), but not the body text only (requires Exchange 2013 - Hello MS advertising).
I'm new to C# and today is my first time to connect to the Exchange Server. I noticed from reading around that "find" is limited in what it can obtain, and that I'll need to bind the message in order to get more information from the email.
Code thus far:
foreach (Item item in findResults.Items)
if (item.HasAttachments) // && item.Attachments[0] is FileAttachment)
{
item.Load();
FileAttachment fileAttachment = item.Attachments[0] as FileAttachment;
date = Convert.ToString(item.DateTimeCreated);
name = Convert.ToString(fileAttachment.Name);
fileAttachment.Load("C:\\test\\" + fileAttachment.Name);
Console.WriteLine(name);
Console.WriteLine(item.Subject);
Console.WriteLine(date);
}
My question from here is if I do EmailMessage msg = EmailMessage.Bind ... what information will I need in order to grab more information?
Solved - for getting sender email and name as well as loading an attachment.
I used the EmailMessage class (just added it in the above loop, and added the variables to the beginning):
EmailMessage msg = (EmailMessage)item;
senderemail = Convert.ToString(msg.Sender.Address);
sendername = Convert.ToString(msg.Sender.Name);
I can then reproduce these on the console:
Console.WriteLine(senderemail);
Console.WriteLine(sendername);
Also, for loading an email's attaachment, I declared a byte[] variable at the beginning, loaded the attachment, converted it, and wrote its content to the console:
fileAttachment.Load();
filecontent = fileAttachment.Content;
System.Text.Encoding enc = System.Text.Encoding.ASCII;
string strFileContent = enc.GetString(filecontent);
Console.WriteLine(strFileContent);
Related
In my application I use some code to send automated mails over our Exchange server.
My current goal is to send a HTML mail with some pictures as a handout. That pictures shall be attachments of the mail.
To this point the code is working (I have changes internal names in the code example):
ExchangeService MailClient = new ExchangeService
{
UseDefaultCredentials = true,
Url = new Uri("https://<domain.of.company>/EWS/Exchange.asmx")
};
EmailMessage msg = new EmailMessage(MailClient);
msg.ToRecipients.Add(new EmailAddress(user.EmailAddress));
msg.Sender = new EmailAddress("sender#domain.of.company");
msg.Subject = "Some text here";
MessageBody Body = new MessageBody
{
BodyType = BodyType.HTML,
Text = NameOfApplication.Properties.Resources.Embedded_HTML_File
};
msg.Body = Body;
msg.SendAndSaveCopy(new FolderId(WellKnownFolderName.SentItems, "sender#domain.of.company")));
With this code the E-Mail get saved in the "Sent" folder and arrives the target mailbox. Success so far.
But as soon I add an attachement by this (no other changes were made)
msg.Attachments.AddFileAttachment(AppContext.BaseDirectory + "Logo.jpg");
SendAndSaveCopy throws an exception with the description "No mailbox with such guid."
When I comment out the AddFileAttachment line the code is working again.
Any idea whats wrong with attachments?
Found the solution. Reading tooltips might help.
Maybe the day will come, when Microsoft will change SendAndSaveCopy to include this step.
But yes: The error message is misleading.
I'm working on an issue in our code base where we want to extract all recipients of an email message received by Outlook. I understand that if recipients are on the BCC line, the receivers of that email don't know about those recipients, but if you're on the BCC line, you do know (it's in the email headers). So I have a message that looks like this:
From: FirstName LastName<name1#host1.tld>
Date: Thu, 16 Jul 2020 09:48:10 +0100
Message-ID: <CAE+EE4GS7RtDKgPGOUbTFQ3=7i9+QiKB++1cx7qqLd_09PRZFg#mail.gmail.com>
Subject: Testing
To: undisclosed-recipients:;
Bcc: name2#host2.tld
Content-Type: multipart/alternative;
Using the Redemption RDOMail.Recipients, I would expect that it would have one item and that it would be name2#host2.tld (since that's all the information I have about recipients of this email). When I actually loop through the recipients, I get a count of 0. Shouldn't it return any BCC addresses it does know about?
Example Code (might be some syntax errors as I'm simplyfing live code):
public IEnumerable<string> GetRecipients(RDOMail mailItem)
{
var recipients = mailItem.Recipients;
for (var i = 0; i < recipients.Count; i++) // never enters as Count == 0
{
var recipient = recipients[i];
var smtpAddress = GetSmtpAddress(recipient);
yield return smtpAddress;
}
}
There is a BCC property of RDOMail, but it is also blank for received mail. The documentation does note: This property contains the display names only. The Recipients collection should be used to modify the BCC recipients. Which leads me to believe that I can use the Recipients collection for Read operations as well.
Yes, I realize I might be able to assume that if the current address isn't on the TO or CC lines, it's on the BCC line. But this doesn't work for shared mailboxes... or rather, I'd have to do a lot of digging to understand where the received mail item is coming from in order to make the correct assumption.
Updated with Solution
I ended up working with Eugene's answer and combined it with looking at the SMTP BCC header for received email as well. There are still some cases (internal email where you're on the BCC header might not contain SMTP headers at all in an On-Prem Exchange Environment), but at this point, I think it's pretty complete. So I still use the above code for GetRecipients(RDOMail mailItem), but after I do that, I also call:
Some Constants
public const string PR_TRANSPORT_MESSAGE_HEADERS = "http://schemas.microsoft.com/mapi/proptag/0x007D001F"
public const string PID_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS = "http://schemas.microsoft.com/mapi/proptag/0x5D08001F"
Test if the receiver is in the BCC
private bool IsReceiverInBcc(RDOMail mailItem)
{
if (mailItem == null) return false;
var receiver = ConvertReceiverToContact(mailItem);
var messageHeaders = mailItem.Fields(Consts(PR_TRANSPORT_MESSAGE_HEADERS)?.ToString();
if (!string.IsNullOrWhiteSpace(messageHeaders)
{
var bccHeaderRegex = new Regex($"{Environment.NewLine}Bcc: .*{Environment.NewLine}", RegexOptions.IgnoreCase);
foreach (Match headerMatch in bccHeaderRegex.Matches(messageHeaders))
{
if (headerMatch?.Value?.IndexOf(receiver.address, CompareOptions.IgnoreCase) >= 0
{
return true;
}
}
}
return false;
}
Extract the current user from the BCC header
private EmailContact ConvertReceiverToContact(RDOMail mailItem)
{
var contact = new EmailContact
{
Address = mailItem.Fields(Consts.PID_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS),
Name = mailItem.ReceivedByName
};
return contact;
}
The Outlook object model (as well as Extended MAPI) doesn't provide any information in the Recipients collection. The Count property returns 0 in this scenario. In the case of Extended MAPI the IMessage::GetRecipientTable method returns an empty table. Here is what MSDN states:
The IMessage::GetRecipientTable method returns a pointer to the message's recipient table, which includes information about all of the recipients for the message. There is one row for every recipient.
So, there are no issues in the Redemption library.
Based on my research performed with a sample email message recieved with the following content in the internet header:
To: undisclosed-recipients:;
Bcc: eugene#somedomain.com
In the case of OOM, you can use the MailItem.ReceivedByName property which returns a string representing the display name of the true recipient for the mail message.
In the case of Extended MAPI (Redemption is a wrapper around this API), you can use the PidTagReceivedRepresentingSmtpAddress property which contains the SMTP email address of the user represented by the receiving mailbox owner. The DASL property name is http://schemas.microsoft.com/mapi/proptag/0x5D08001F.
No, even BCC recipients won't be in the BCC header. You can generate the BCC header when saving in the MIME format, but it won't be present in messages received from a POP3 or IMAP4 server.
Generally, when a message is sent through SMTP, the server does not look at the MIME headers to figure out the recipients. The recipients are determined by the RCPT TO SMTP command. And no email client specifies BCC header in the outgoing messages - that would be a bug.
I'm currently using Exchange Web Services in C#. I basically have a small application that reads emails from an inbox and process them.
I would like to forward those emails I receive as an attachment of an email. This attachment will be an outlook email that will include the original email (including its own attachments if any).
Any ideas?
Thanks!
EDIT:
Not sure why I'm getting the down votes but it seems this is not possible as the EWS API does not provide such functionality
You can create an ItemAttachment with EWS but you can't replicate fully what is possible in Outlook with MAPI. Eg with EWS you can create an ItemAttachment and then use the MIMEContent to create an attachment based on a current message as a workaround eg
FolderId Inboxid = new FolderId(WellKnownFolderName.Inbox, "target#domain.com");
ItemView InboxItemView = new ItemView(1);
FindItemsResults<Item> inFiResuls = service.FindItems(Inboxid,InboxItemView);
if(inFiResuls.Items.Count == 1){
EmailMessage fwdMail = new EmailMessage(service);
EmailMessage orgMail = (EmailMessage)inFiResuls.Items[0];
PropertySet psPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
psPropSet.Add(ItemSchema.MimeContent);
orgMail.Load(psPropSet);
ItemAttachment emAttach = fwdMail.Attachments.AddItemAttachment<EmailMessage>();
emAttach.Item.MimeContent = orgMail.MimeContent;
ExtendedPropertyDefinition pr_flags = new ExtendedPropertyDefinition(3591,MapiPropertyType.Integer);
emAttach.Item.SetExtendedProperty(pr_flags,"1");
emAttach.Name = orgMail.Subject;
fwdMail.Subject = "see Attached";
fwdMail.ToRecipients.Add("user#domain.com");
fwdMail.Send();
}
This however doesn't give full fidelity of all the mapi properties associated with a particular message as the MIMEContent is just that, for most normal email messages this isn't an issue however for a message with an attached Outlook Task or other TNEF attachment then you would loose these of attachments or for other properties like categories,flags you would loose these also (you could copy these manually if needed).
Cheers
Glen
you can forward your email using this way. It first loads and reads the each emails with attachment who has "msg" extension. then forwards it to given address. see the below code
FindItemsResults<Item> findResults = exchange.FindItems(WellKnownFolderName.Inbox, newItemView(50,50));
Item[] msgItems = findResults.Where(msgItem => msgItem.HasAttachments).ToArray();
EmailMessage msgInfo = null;
var fileExtensions = new List<string> { "msg", "MSG", "Msg" };
foreach (MSEWS.Item msgItem in msgItems )
{
msgInfo = EmailMessage.Bind(exchange, msgItem.Id);
FileAttachment fa = msgInfo.Attachments[0] asFileAttachment;
if (fileExtensions.Any(ext => ext.Contains(fa.Name.Substring(fa.Name.Length - 3))))
{
fa.Load();
ResponseMessage responseMessage = msgInfo.CreateForward();
responseMessage.BodyPrefix = "messageBody";
responseMessage.ToRecipients.Add("toAddress");
responseMessage.Subject = "subject";
responseMessage.SendAndSaveCopy();
}
}
I have an application that has been working fine for several weeks, basically it polls an Exchange 2010 mail server mailbox for new messages, gets the details of each one and any attachments, and use that information to create cases (with attachments) on the SalesForce CRM.
After each case is created from the email, the email must be marked as read by retrieving it from Exchange using the stored Item ID and calling the property set.
For several weeks this has been working fine, even getting some extra properties from Exchange such as the Subject, From, To properties which I can use in the session log.
Suddenly today the mail isn't being returned by Exchange from the itemID if I try to include the PropSet - if I omit the PropSet it works fine.
here is the code:
try
{
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(common.strInboxURL); // new Mailbox(targetEmailAddress); #"bbtest#bocuk.local"
//creates a folder object that will point to inbox fold
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Microsoft.Exchange.WebServices.Data.Folder inbox = Microsoft.Exchange.WebServices.Data.Folder.Bind(service, fid);
// As a best practice, limit the properties returned to only those that are required.
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Subject, EmailMessageSchema.IsRead, EmailMessageSchema.Sender, EmailMessageSchema.DateTimeReceived);
// Bind to the existing item by using the ItemId.
// This method call results in a GetItem call to EWS.
EmailMessage mail = EmailMessage.Bind(service, itemId); //, propSet);
if (!mail.IsRead) // check that you don't update and create unneeded traffic
{
mail.IsRead = true; // mark as read
mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
}
mail.Update(ConflictResolutionMode.AlwaysOverwrite);
e2cSessionLog("\tcommon.MarkAsRead", "email Item ID: " + itemId);
//e2cSessionLog("\tcommon.MarkAsRead", "MARKED AS READ email Subject: " + mail.Subject.ToString() + "; From: " + mail.Sender.Name, mail.DateTimeReceived.ToString());
return true;
}
catch (Exception ex)
{
string innerException = "";
...
notice that in the line EmailMessage mail = EmailMessage.Bind(service, itemId); //, propSet); I have now omitted the PropSet argument and it now works.
But why?
Is there something else I need to do in order to always get the properties back with the mail returned using Item ID?
Any thoughts?
I've noticed that when someone copy paste an email and then sends it the "src" value of the images changes. For example if the original email has an image which is an attachment and its contentId is: cid:companlyLogo. And then someone copy paste this email into a new draft and sends it this src value changes to: src="cid:image001.jpg#01CCF6B1.A4CA2FE0".
I have no clue whatsoever on how to get this image and save it in an image object in c#. Am currently using EWS api for doing this. Problem is that since its a copy paste it no longer has attachments as the original email.
Does anyone has any idea on how to retrieve the images of this types of emails?
Embedded images are treated as email attachments by Exchange. That means you can retrieve the images from the Item.Attachments property. The following example shows you how to do this using the EWS Managed API. Notice that EWS will not load the attachments unless you explicitly tell it to by calling LoadPropertiesForItems.
You can tell that an attachment is embedded by checking the Attachment.IsInline property. EWS lets you load the attachment and save it to disk by calling the FileAttachment.Load method.
ExchangeService service = GetService();
var view = new ItemView(1);
var searchFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.Subject, "Some subject text");
var items = service.FindItems(WellKnownFolderName.Inbox, searchFilter, view);
service.LoadPropertiesForItems(items, new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.Attachments));
var item = items.ElementAt(0) as EmailMessage;
for (int i = 0; i < item.Attachments.Count; i++)
{
var att = item.Attachments[i] as FileAttachment;
if (att.IsInline && att.ContentType.Contains("image"))
{
att.Load(String.Format(#"c:\temp\attachedimage_{0}.jpg", i));
}
}