This question already has answers here:
Wrong mailbox items being retrieved using Exchange Web Services managed API in C#
(2 answers)
Closed 8 years ago.
I am trying to read a particular email account to find items with attachments that are unread, and have certain words in the subject.
I have the below code that works but only reads my inbox and not the inbox I want to check
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback=CertificateValidationCallBack;
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);// .Exchange2007_SP1);
service.UseDefaultCredentials = true;
service.AutodiscoverUrl("bbtest#domainname.com"); //, RedirectionUrlValidationCallback); //bbtest#bocuk.local
ItemView view = new ItemView(1);
string querystring = "HasAttachments:true Kind:email";
//From:'Philip Livingstone' Subject:'ATTACHMENT TEST' Kind:email";
// Find the first email message in the Inbox that has attachments.
// This results in a FindItem operation call to EWS.
FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Inbox, querystring, view);
foreach (EmailMessage email in results)
if (email.IsRead == false) // && email.HasAttachments == true
{
System.Diagnostics.Debug.Write("Found attachemnt on msg with subject: " + email.Subject);
// Request all the attachments on the email message.
//This results in a GetItem operation call to EWS.
email.Load(new PropertySet(EmailMessageSchema.Attachments));
foreach (Attachment attachment in email.Attachments)
{
if (attachment is FileAttachment)
...
what do I have to change so that my code will read the messages in the inbox of bbtest#domainname.com?
By default your mailbox' inbox folder will be set as root when calling FindItems on ExchangeService object. Assuming your domain account has the necessary permissions to access the other mailbox this below should do the trick. I have ommited the service creation part.
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(#"othermailbox#domain.com");
//creates a folder object that will point to inbox folder
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Folder inbox = Folder.Bind(service, fid);
//load items from mailbox inbox folder
if(inbox != null)
{
FindItemsResults<Item> items = inbox.FindItems(new ItemView(100));
foreach(var item in items)
Console.WriteLine(item);
}
Related
I'm trying to fetch all messages from a user-specified date on an Exchange 2016 server using the EWS managed API in C#.
I authenticate with:
public static void Login(string username, string password)
{
service.UseDefaultCredentials = false;
service.Credentials = new WebCredentials(username, password);
service.AutodiscoverUrl(username, RedirectionUrlValidationCallback);
}
Then select the appropriate inbox with
sharedMailbox = new Mailbox(Properties.Settings.Default.Inbox);
the SMTP address is stored in Settings.settings .
I then find the desired folder using the following (from this thread):
targetFolderId = new FolderId(WellKnownFolderName.Inbox, sharedMailbox);
// set folder view
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
view.PropertySet.Add(FolderSchema.DisplayName);
view.Traversal = FolderTraversal.Deep;
folderResults = service.FindFolders(WellKnownFolderName.Inbox, view);
foreach(Folder f in folderResults)
{
if(f.DisplayName == "Invoices")
{
targetFolderId = f.Id;
//tried showing a message box here
}
}
And use the following (filter code from here and retrieve details from Exchange server code from here) to get the messages I want:
public static void FetchUnreadMessages(DateTime searchDate)
{
SearchFilter greaterthanfilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, searchDate);
SearchFilter lessthanfilter = new SearchFilter.IsLessThan(ItemSchema.DateTimeReceived, searchDate.AddDays(1));
SearchFilter dayFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, greaterthanfilter, lessthanfilter);
results = service.FindItems(targetFolderId, dayFilter, view);
foreach(var item in results.Items)
{
emails.Add((EmailMessage)item);
}
PropertySet properties = (BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(emails, properties);
}
I'm not sure where this is breaking down. I tried showing a message box in the foreach loop that finds the folder with the specified name, and it appears the folder is never found. I know there is a folder with that display name in the shared inbox.
I'm not great at debugging and unfortunately my grasp of the EWS API is pretty shaky. Any suggestions as to what I'm missing are welcome.
I keep everything related to the inbox in a static class so I only have to worry about one instance.
There is some problems with you code first
Then select the appropriate inbox with
sharedMailbox = new Mailbox(Properties.Settings.Default.Inbox);
targetFolderId = new FolderId(WellKnownFolderName.Inbox, sharedMailbox);
Nothing wrong with this code but you probably need to understand what's happening here. This code doesn't make any calls to the server it just setups a FolderId class that you can use in a call to get one of the well known folder.
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
view.PropertySet.Add(FolderSchema.DisplayName);
view.Traversal = FolderTraversal.Deep;
folderResults = service.FindFolders(WellKnownFolderName.Inbox, view);
This code would just search the Inbox Subfolder of the Mailbox who's credentials you are using. eg
service.Credentials = new WebCredentials(username, password);
If you wanted to Search the SubFolders of the Inbox of the SharedMailbox you would use
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
view.PropertySet.Add(FolderSchema.DisplayName);
view.Traversal = FolderTraversal.Deep;
folderResults = service.FindFolders(targetFolderId , view);
because you are using the targetFolderId in the FindFolder operations that is telling Exchange to Search the Shared Mailbox rather then the mailbox associated with the credentials you are using.
I was able to succeed via a package I found called EAGetMail. Unfortunately, I realized soon after that they have a token system and this is not a free approach.
There are a couple other choices available, like using Outlook Mail REST API, and MimeKit, but I'm lost on how to achieve my end result because no "start to finish" code is available on either of these references that demonstrates how to parse an Inbox for an account.
I've started to write this with the help of Mimekit, but am not sure if this is the proper way at all.
I must imagine it looks something like:
using (var client = new SmtpClient ())
{
client.Connect("outlook.office365.com", 587);
client.Authenticate("myemail#office365account.com", "mypassword");
var message = MimeMessage.Load(stream);
}
I don't know how to setup the stream mentioned above, and I don't know if it's possible to do this with Mimekit and Office 365.
I'm open to seeing a solution for this in any other approach that's not through EAGetMail. If anyone has a lightweight solution ranging from actual establishing a connection, to pulling messages from the inbox, would be great to see!
I've got it using EWS (Exchange Web Services). Here's my code:
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("email#myemail.com", "myPassword");
service.AutodiscoverUrl("email#myemail.com", RedirectionUrlValidationCallback);
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(#"email#myemail.com");
//creates a folder object that will point to inbox folder
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Folder inbox = Folder.Bind(service, fid);
//load items from mailbox inbox folder
if (inbox != null)
{
FindItemsResults<Item> items = inbox.FindItems(new ItemView(100));
foreach (var item in items)
{
item.Load();
Console.WriteLine("Subject: " + item.Subject);
}
}
}
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.
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?