I'm using Exchange web service to read email from Office365. The C# program works fine. I can get EmailMessage in Inbox, and get their subject and message body, etc. However, I could not figure out the way to check whether the message is a bounce back message or not.
Do I have to parse the message body to see whether there are some special sentence, ie. Mail Delivery Failure? If so, is it possible different email servers bounce back emails with different words? i.e some use 'Mail Delivery Failure', some use 'Mail Delivery Not Succeeded'? (just an example, I do not know whether this is true)
Or, the message object has an attribute that can be used for this purpose?
Thanks
*** Just found that the exchange webservice can not see 'Bounce back' messages in INBOX. I'm using below code, all messages can be 'seen' except Bounce Back ones. Do I miss anything to filter te bounce back messages? They are actually in INBOX, unread, and I can see it from Office365 page.
private static void ProcessEmailMessages(SearchFolder searchFolder, Folder folderHistory, Folder folderBounceBack)
{
if (searchFolder == null)
{
return;
}
const Int32 pageSize = 50;
ItemView itemView = new ItemView(pageSize);
PropertySet itempropertyset = new PropertySet(BasePropertySet.FirstClassProperties);
itempropertyset.RequestedBodyType = BodyType.Text;
itemView.PropertySet = itempropertyset;
PropertySet propertySet = new PropertySet(BasePropertySet.IdOnly, FolderSchema.DisplayName);
folderHistory.Load(propertySet);
folderBounceBack.Load(propertySet);
FindItemsResults<Item> findResults = null;
do
{
findResults = searchFolder.FindItems(itemView);
foreach (Item item in findResults.Items)
{
if (item is EmailMessage)
{
// load body text
item.Load(itempropertyset);
EmailMessage email = item as EmailMessage;
//email.Move(folder.Id);
// check email subject to find the bounced emails
bool subjectContains = Regex.IsMatch(email.Subject, "Mail Delivery Failure", RegexOptions.IgnoreCase);
bool bodyContains = Regex.IsMatch(email.Subject, "Delivery", RegexOptions.IgnoreCase);
if (subjectContains || bodyContains)
{
email.Move(folderBounceBack.Id);
Console.WriteLine("Move the Bounced email: {0}", email.Subject);
ShowMessageInfo(email);
}
else
{
email.Move(folderHistory.Id);
Console.WriteLine(">>> Keep the email: {0}", email.Subject);
}
}
}
itemView.Offset += pageSize;
} while (findResults.MoreAvailable);
}
Check the ItemClass attribute. Messages like that should have a class that contains "REPORT" in it.
I use EWS in an O365 environment to help process bounce backs and use the following code to just get the NDRs from a users' inbox.
var sf = new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "REPORT.IPM.Note.NDR");
var SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, sf);
var view = new ItemView(1000) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
var findResults = service.FindItems(WellKnownFolderName.Inbox, searchFilter, view);
Hope it helps.
Related
I have a service running as a scheduled job on a machine. It's running under a service account that doesn't have it's own mailbox. We would like it to send emails from the team's shared inbox.
Using impersonation here is what I tried.
var service = new ExchangeService
{
TraceEnabled = true,
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, Resources.EmailUsername)
};
service.AutodiscoverUrl(Resources.EmailUsername, RedirectionUrlValidationCallback);
var email = new EmailMessage(service);
if (!string.IsNullOrWhiteSpace(recipients))
{
foreach (var recipient in recipients.Split(','))
{
email.ToRecipients.Add(recipient.Trim());
}
}
email.Subject = subject;
email.Body = new MessageBody(BodyType.HTML, body);
if (attachmentName != null && attachment != null)
{
email.Attachments.AddFileAttachment(attachmentName, attachment);
}
email.Send();
It fails and I get an exception saying:
When making a request as an account that does not have a mailbox, you
must specify the mailbox primary smtp address for any distinguished
folder Ids.
The TraceEnabled part led me to notice MessageDisposition=SaveOnly was set in the xml. I looked up MessageDisposition and figured I wanted SendOnly.
After much searching I ended up here: https://github.com/OfficeDev/ews-managed-api/blob/master/Core/ServiceObjects/Items/EmailMessage.cs
Which shows:
public void Send()
{
this.InternalSend(null, MessageDisposition.SendOnly);
}
Well that looks like what I wanted in the first place... But then:
private void InternalSend(FolderId parentFolderId, MessageDisposition messageDisposition)
{
this.ThrowIfThisIsAttachment();
if (this.IsNew)
{
if ((this.Attachments.Count == 0) || (messageDisposition == MessageDisposition.SaveOnly))
{
this.InternalCreate(
parentFolderId,
messageDisposition,
null);
}
else
{
// If the message has attachments, save as a draft (and add attachments) before sending.
this.InternalCreate(
null, // null means use the Drafts folder in the mailbox of the authenticated user.
MessageDisposition.SaveOnly,
null);
this.Service.SendItem(this, parentFolderId);
}
}
...
The two comments are the most enlightening parts. The attachment is being saved to the drafts folder of the user running the process.
To get around this, the message must already be saved when we call Send. Let's make sure it is saved in the mailbox we know exists. So we remove the impersonation, add a step to save it, and modify the From field. Then we can safely send the message, and it will remove itself from the draft folder.
var service = new ExchangeService
{
TraceEnabled = true
};
service.AutodiscoverUrl(Resources.EmailUsername, RedirectionUrlValidationCallback);
var email = new EmailMessage(service);
if (!string.IsNullOrWhiteSpace(recipients))
{
foreach (var recipient in recipients.Split(','))
{
email.ToRecipients.Add(recipient.Trim());
}
}
email.Subject = subject;
email.Body = new MessageBody(BodyType.HTML, body);
if (attachmentName != null && attachment != null)
{
email.Attachments.AddFileAttachment(attachmentName, attachment);
}
var folderId = new FolderId(WellKnownFolderName.SentItems, Resources.EmailUsername);
email.Save(folderId);
email.From = Resources.EmailUsername;
email.Send();
I'm connecting fine to the Exchange Server and I'm getting all the unread mails but a new message doesn't want to send
Message Code
var newHTML = html.HTMLCode.Replace("{House}", house.Number)
.Replace("{Token}", token.Number)
.Replace("{contactPerson}",
string.Format("<a href=mailto:{0}>{1}</a>",
contactPerson, contactPerson));
LogError.WriteToFile("Has House and token");
//Send mail with token to user
EmailMessage message = new EmailMessage(emailService);
message.ToRecipients.Add(email.From.Address);
message.Subject = string.Format("Electricity token for: {0}", house.Number);
message.Body = new MessageBody(html.HTMLCode);
LogError.WriteToFile("Trying to send");
message.Send();
I have a try catch around this so in the log file I get "Trying to send" but then a error occurs that reads as
"EmailAddress or ItemId must be included in the request."
From examples seen, the way I construct my message seems sufficient but clearly isn't
This is how I got all my unread emails
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
ItemView view = new ItemView(int.MaxValue);
FindItemsResults<Item> findResults = emailService.FindItems(WellKnownFolderName.Inbox, sf, view);
foreach (EmailMessage email in findResults)
{}
Note that I got them as a "EmailMessage"
But were never able to get the senders email address so that's why it didn't want to send.
and then I found this article: FindItem returns only the first 512 bytes of any streamable property
So then I went to go get that specific email like this. Note that I now get the "Item" from the findResults and with that "Item" I do the following.
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
ItemView view = new ItemView(int.MaxValue);
FindItemsResults<Item> findResults = emailService.FindItems(WellKnownFolderName.Inbox, sf, view);
foreach (Item item in findResults)
{
//Get the email message
EmailMessage email = EmailMessage.Bind(emailService, item.Id,
new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Attachments));
if (email != null)
{}
}
Now I could finally send emails
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'm currently pulling emails from an exchange inbox like so...
var exchangeService = new ExchangeService(ExchangeVersion.Exchange2007_SP1)
{
Credentials = new NetworkCredential("user", "password", "domain")
};
exchangeService.AutodiscoverUrl("user#domain.com");
var emails = exchangeService.FindItems(WellKnownFolderName.Inbox, new ItemView(5));
foreach (var email in emails)
{
//var senderEmail = email.???
}
The email object doesn't seem to have any property for getting the sender's email address. How do I get that?
Here's some quick source I pulled from a working project example.
Basically, you can get minor details just by casting your result to an EmailMessage. However if you want to get richer details about the sender (display name, etc.) then you have to make a specific, additional bind (Web service request) against the message.
findResults = exchangeService.FindItems(folder.Id, messageFilter, view);
foreach (Item item in findResults)
{
if (item is EmailMessage)
{
EmailMessage message;
if (!toFromDetails)
message = (EmailMessage)item;
else
message = EmailMessage.Bind(exchangeService, item.Id);
As you can see in this code, I have an option to perform the additional bind, because it can take awhile, and I'm often dealing with thousands of results from hundreds of mailboxes. Sometimes the additional time may not be worth it to a particular customer.
Can anyone tell me how to get an email body, receipt, sender, CC info using Exchange Web Service API? I only know how to get subject.
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
service.Credentials = new NetworkCredential("user", "password", "domain");
service.Url = new Uri("https://208.243.49.20/ews/exchange.asmx");
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
FindItemsResults<Item> findResults = service.FindItems(
WellKnownFolderName.Inbox,
new ItemView(10));
foreach (Item item in findResults.Items)
{
div_email.InnerHtml += item.Subject+"<br />";
}
My development environment is asp.net c# Exchange-server 2010
Thank you.
Since the original question specifically asked for "email body, receipt, sender and CC info," I thought I would address those. I assume "receipt" is recipient info, and not the "notify sender" feature of email that no one uses. CC looks like it is handled the same way as recipients.
I liked Henning's answer to reduce the function to two calls, but had a little bit of difficulty figuring out how to handle a PropertySet. Google search was not immediately clear on this, and I ended up using someone else's tutorial:
// Simplified mail item
public class MailItem
{
public string From;
public string[] Recipients;
public string Subject;
public string Body;
}
public MailItem[] GetUnreadMailFromInbox()
{
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, new ItemView(128));
ServiceResponseCollection<GetItemResponse> items =
service.BindToItems(findResults.Select(item => item.Id), new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.From, EmailMessageSchema.ToRecipients));
return items.Select(item => {
return new MailItem() {
From = ((Microsoft.Exchange.WebServices.Data.EmailAddress)item.Item[EmailMessageSchema.From]).Address,
Recipients = ((Microsoft.Exchange.WebServices.Data.EmailAddressCollection)item.Item[EmailMessageSchema.ToRecipients]).Select(recipient => recipient.Address).ToArray(),
Subject = item.Item.Subject,
Body = item.Item.Body.ToString(),
};
}).ToArray();
}
Using FindItems will only get you so far, because it does only return the first 255 bytes of a body. What you should do is a combination of FindItem to request the ids of the mails and issue one or more GetItem calls to get the properties you are interested in.
Instead of using the ExtendedProperties, you could also cast to EmailMessage and read the property you want directly. For example the sender address:
((Microsoft.Exchange.WebServices.Data.EmailMessage)(item)).From.Address;
here you will find the solution.
http://blogs.msdn.com/b/akashb/archive/2010/03/05/how-to-build-a-complex-search-using-searchfilter-and-searchfiltercollection-in-ews-managed-api-1-0.aspx
// Send the request to search the Inbox and get the results.
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, FinalsearchFilter, view);
// Process each item.
if (findResults.Items.Count > 0)
{
foreach (Item myItem in findResults.Items)
{
if (myItem is EmailMessage)
{
Console.WriteLine((myItem as EmailMessage).Subject);
}
if (myItem.ExtendedProperties.Count > 0)
{
// Display the extended property's name and property.
foreach (ExtendedProperty extendedProperty in myItem.ExtendedProperties)
{
Console.WriteLine(" Extended Property Name: " + extendedProperty.PropertyDefinition.Name);
Console.WriteLine(" Extended Property Value: " + extendedProperty.Value);
}
}
}
}
else
{
Console.WriteLine("No Items Found!");
}
}