Search attachments from a mailbox using EWS Managed API and C# - c#

I want to search all attachments from a mailbox having certain keywords in their name.I am doing this using C# EWS Managed API(version 2.2).
I am able to access the Item with attachments using Item.HasAttachment:true property and the code is working as expected. But the processing time is very long.
The current process flow is :
1.Get all the folders from a mailbox.
2.For each folder, search items having attchments (using Item.HasAttachment:true searcFilter).
3.Check whether the Attachment name contains the keywords.
I need to know if there is a better and faster way to access attachments in a mailbox/folder using EWS. Instead of checking every mail item, is there a way to apply filter for attachments on folder level?
Below is the code snippet used to fetch the attachemnts by name keyword
SearchFilter searchFilter = new SearchFilter.IsEqualTo(ItemSchema.HasAttachments, true); //SearchFilter for finding item with attachments
FindItemsResults<Item> searchResults = null;
FileAttachment fileAttachmentobj = null;
ItemAttachment itemAttachmentobj = null;
for (var j = 0; j < folder.Count; j++) //Looping for all the folders in a mailbox
{
for (int i = 0; i < strAttachNameKeyword.Length; i++) //Looping for keywords to be searched
{
searchResults = service.FindItems(folder[j].Id, searchFilter, view);
if (searchResults.TotalCount > 0)
{
service.LoadPropertiesForItems(searchResults, new PropertySet(BasePropertySet.IdOnly, ItemSchema.HasAttachments));
foreach (Item item in searchResults) //Processing each item in SearchResults
{
item.Load();
foreach (Attachment attachmentObj in item.Attachments) //for each attachment in an item
{
//attachmentObj.Load();
fileAttachmentobj = attachmentObj as FileAttachment;
itemAttachmentobj = attachmentObj as ItemAttachment;
if (fileAttachmentobj != null && (fileAttachmentobj.Name.Contains(strAttachNameKeyword[i])))
{
//fileAttachmentobj.Load();
Console.WriteLine(fileAttachmentobj.Name);
Console.WriteLine(fileAttachmentobj.Size);
Console.WriteLine(fileAttachmentobj.Id);
}
}
}
}
}
}

I'd suggest you use a QueryString instead of a SearchFilter which means you'll be doing a Content Index search rather the a folder restriction which is much faster. eg
FindItemsResults fiItems = service.FindItems(QueryFolder, "Attachment:blah.pdf", iv);
You can easily test this using the EWSEditor https://github.com/dseph/EwsEditor/releases if you right click a folder->search for items and select the AQS radio button it has a simple interface for AQS/KQL queries

Related

c# split/duplicate multi attachment outlook emails to individual emails with one attachment each

I am working on a workflow that deals with each PDF attachment as a separate order. It is written by others and I have no control over what happens in it. All it requires is that each of the outlook email must have one and only one PDF attachment.
As I can not amend the workflow, I have to split/duplicate any email that has multiple (2 or more) attachments and save copies of that original email one with each attachment.
I have tried the following, it creates copies but doesn't get rid of additional attachments from the copy.
foreach (var i in ordersFolder.Items)
{
if((i is MailItem) == false)
{
continue;
}
MailItem item = (MailItem)i;
if (item.Attachments.Count > 1)
{
foreach (var attachment in item.Attachments)
MailItem newMail = (MailItem)item.Copy(); //this works
var restAttachments = newMail.Attachments.Where(x => x.FileName != attachment.FileName).ToList(); //this gives me list of all unwanted ones
foreach (var restAttachmentItem in restAttachments)
{
var attachmentToRemove = newMail.Attachments.FirstOrDefault(x => x.FileName == restAttachmentItem.FileName);
var indexToRemove = attachmentToRemove.Index; //this returns 2
newMail.Attachments.Remove(indexToRemove); //this doesn't work
}
}
}
//note - I noticed that there is a difference between zero-based index and 1 based index. so, after copying the email I tried the following and still didn't work
newMail.Attachments.Remove(1); //no effect
newMail.Attachments.Remove(2); //no effect
//If you can help, please also, help with deleting the original multi-attachment email (MailItem item)
Based on my test, you can try the following code to remove the Specified attchment.
foreach (Outlook.MailItem item in testFolder.Items)
{
if(item.Attachments.Count>1)
{
foreach (Outlook.Attachment attachment in item.Attachments)
{
if(!attachment.FileName.Contains(".png"))
{
Outlook.MailItem newMail = item.Copy();
var restAttachments = newMail.Attachments.Cast<Outlook.Attachment>().Where(x => x.FileName != attachment.FileName).ToList(); //this gives me list of all unwanted ones
foreach (var restAttachmentItem in restAttachments)
{
var attachmentToRemove = newMail.Attachments.Cast<Outlook.Attachment>().FirstOrDefault(x => x.FileName == restAttachmentItem.FileName);
var indexToRemove = attachmentToRemove.Index;
newMail.Attachments.Remove(indexToRemove);
newMail.Save();
}
}
}
}
}
The code if(!attachment.FileName.Contains(".png")) is used to filter the png file out.
Note: Please remember save your email after you delete the attachment by using newMail.Save method.

Get mail from folder within Inbox - EWS

I want to access emails in a folder called "ITServiceDesk" in my exchange inbox.
I can access the folder but i cant figure out how to read the mail inside that folder.
I am accessing the folder here:
var view = new FolderView(100);
view.Traversal = FolderTraversal.Deep;
var fileview = new ItemView(100);
var filter = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "ITServiceDesk");
// Read 100 mails
foreach (var item in _service.FindFolders(WellKnownFolderName.Inbox, filter, view))
{
MessageBox.Show(item.DisplayName);
foreach (EmailMessage email in _service.FindItems(WellKnownFolderName.Inbox, filter, fileview))
{
email.Load(new PropertySet(EmailMessageSchema.ConversationTopic, ItemSchema.Attachments,
ItemSchema.TextBody));
MessageBox.Show(email.ConversationTopic);
MessageBox.Show(email.TextBody);
}
}
Nothing happens when i get inside the second foreach loop. The message box shows that it can find the folder as the item.displayname is correct.
If you are finding the folder with you code then just call the findItem method on the Folder object that is returned eg
foreach (var Folder in _service.FindFolders(WellKnownFolderName.Inbox, filter, view))
{
MessageBox.Show(Folder.DisplayName);
foreach (EmailMessage email in Folder.FindItems(fileview))
{
email.Load(new PropertySet(EmailMessageSchema.ConversationTopic, ItemSchema.Attachments,
ItemSchema.TextBody));
MessageBox.Show(email.ConversationTopic);
MessageBox.Show(email.TextBody);
}
}
Here is an example from my website:
FindItemsResults<Item> findResults
= service.FindItems(WellKnownFolderName.Inbox, new ItemView( 10 ) );
foreach ( Item item in findResults.Items )
Console.WriteLine( item.Subject );
See C#: Getting All Emails From Exchange using Exchange Web Services

Use attachments from calendar items - Outlook - C#

I'm trying to use the attachments included in calendar items pulled progmatically.
I have a list of chosen calendar subject lines from a previous dialog box, and while the subject is transferring properly, the body isn't working well (another question altogether) but the attachments aren't working whatsoever.
Here's my foreach loop where the attachments are being placed into an Attachments array for use later:
string[] subjects = new string[dialog.chosen.Count];
string[] bodies = new string[dialog.chosen.Count];
Attachments[] attach = new Attachments[dialog.chosen.Count];
foreach (Outlook.AppointmentItem appt in rangeAppts)
{
foreach (string text in dialog.chosen)
{
if (text == appt.Subject)
{
subjects[i] = appt.Subject;
bodies[i] = Convert.ToString(appt.Body);
attach[i] = appt.Attachments;
i = i + 1;
}
}
}
And then here's where I actually call the method:
sendEmailTemplate(bodies[i], subject, to, "", attachment: attach[i]);
And then the method itself:
public void sendEmailTemplate(string body, string subject, string to, string cc , Attachments attachment = null)
{
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook._MailItem oMailItem = (Microsoft.Office.Interop.Outlook._MailItem)oApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
oMailItem.HTMLBody = body;
oMailItem.Subject = subject;
try
{
oMailItem.Attachments.Add(attachment);
}
catch {}
oMailItem.To = to;
oMailItem.CC = cc;
oMailItem.Display(false);
oMailItem.Application.ActiveInspector().WindowState = Microsoft.Office.Interop.Outlook.OlWindowState.olNormalWindow;
}
I've tried several things, however when I actually go to send the e-mail, I end up getting:
Exception: Member not found. HRESULT: 0x80020003
And then I haven't been able to get anything else to work. The try/catch loop on the method is to prevent the above exception as I was getting that exception regardless of whether or not an attachment was present, and now attachments just aren't being added.
I'm using Interop that comes with Office along with C#. Winforms if that makes a difference.
MailItem.Attachments takes either a string (fully qualified file name), or another Outlook item (MailItem, ContactItem, etc.).
You cannot pass Attachments object as an argument. If you need to copy the attachments, loop through all attachments in the Attachments collection, call Attachment.SaveAsFile for each attachment, pass the file name to MailItem.Attachments.Add, delete thee temporary file.

EWS Managed API: Identify deleted email when fetching from "AllItems" folder

I am using EWS managed API with C# to fetch emails from user accounts. I am fetching the emails from "AllItems" folder and getting different email properties such as subject, datetimesent, etc.
"AllItems" folder also contains emails that are deleted and are in "DeletedItems" folder. I would like to identify whether the email is deleted (i.e. it is in "DeletedItems" folder) and if possible, when the email was deleted.
Below is the code I am using. I could not find a property that would identify whether the email is deleted.
FolderView viewFolders = new FolderView(int.MaxValue) { Traversal = FolderTraversal.Deep, PropertySet = new PropertySet(BasePropertySet.IdOnly) };
ItemView viewEmails = new ItemView(int.MaxValue) { PropertySet = new PropertySet(BasePropertySet.IdOnly) };
SearchFilter folderFilter = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "AllItems");
FolderId rootFolderId = new FolderId(WellKnownFolderName.Root);
FindItemsResults<Item> findResults;
FindFoldersResults AllItemsFolder= service.FindFolders(WellKnownFolderName.Root, folderFilter, viewFolders);
if (AllItemsFolder.Count() > 0)//if we have AllItems folder
{
foreach (Folder folder in AllItemsFolder.Folders)
{
ItemView itv = new ItemView(int.MaxValue);
findResults = service.FindItems(folder.Id, itv);
foreach (Item item in findResults)
{
if (item is EmailMessage)
{
MessageBox.Show(item.Subject);
// Show whether the message is in deleted folder and when message was deleted
}
}
}
}
As you state, I don't think there is a property like that for mail items.
I would use the GetFolder operation with the well known folder name "deleteditems" to get the Id of that folder. Then, I would ignore all mail items that has this Id as ParentFolderId.

How to retrieve list item attachments with SharePoint 2013 Event Receiver in correct order

I've created a Standard SharePoint 2013 Event Receiver on a custom list.
Watched Event = "ItemAdded".
Later in my code I need to retrieve the attachments of the list item in the same order as the user inserted them. But unfortunately it seems that SharePoint does not do this by Default.
Example:
The User creates a list item and attach the following files
Picture_front.jpg
Picture_back.png
Picture_231.jpg
Now in my Event Receiver it is possible that I first get 'Picture_back' then 'Picture_front'... or in any other order.
How can I retrieve the attachments in the same order as they have been attached to the list item?
I tried to use the SPFile Property 'TimeCreated' but this is also not working... They got the same timestamp :( (also if I am using 'Ticks')
Any ideas or I am doing something wrong?
Here is my Code:
public override void ItemAdded(SPItemEventProperties properties)
{
SPAttachmentCollection attachments = properties.ListItem.Attachments;
if (attachments.Count > 0)
{
int p = 1;
Dictionary<string, string> attachementDict = new Dictionary<string, string>();
try
{
foreach (string attachement in attachments)
{
SPFile attachementFile = properties.ListItem.ParentList.ParentWeb.GetFile(properties.ListItem.Attachments.UrlPrefix + attachement);
string imageUrlPath = properties.WebUrl + attachementFile.ServerRelativeUrl;
string imageTimestamp = attachementFile.TimeCreated.Ticks.ToString();
// This Dict is used lator for sorting
// but at the Moment I get here the error that the same key already exists because of the same timestamp of the files :(
attachementDict.Add(imageTimestamp, imageUrlPath);
}
}
catch (Exception ex)
{
// SPLog
}
}
here my code ..i hope it help you!
try
{
string strUrl = SPContext.Current.Site.Url + "/" + subSite;
using (SPSite Site = new SPSite(strUrl))
{
using (SPWeb Web = Site.OpenWeb())
{
SPList List = Web.Lists[listName];
SPListItem item = List.GetItemById(ID);
foreach (String attachmentname in item.Attachments)
{
AnnouncementsCommon objAnnouncementsCommon = new AnnouncementsCommon();
String attachmentAbsoluteURL = item.Attachments.UrlPrefix + attachmentname;
objAnnouncementsCommon.AttachmentName = attachmentname;
objAnnouncementsCommon.AttachmentURL = attachmentAbsoluteURL;
lstAnnouncementsCommon.Add(objAnnouncementsCommon);
}
}
}
}
catch (Exception Exc)
{
Microsoft.Office.Server.Diagnostics.PortalLog.LogString("SSC DAL Exception Occurred: {0} || {1}", Exc.Message, Exc.StackTrace);
}
return lstAnnouncementsCommon;
}
As an alternative approach you could use your receiver to store the attachments in a picture library and add two fields to this library: a lookup column to your original custom list item and an option column with "default", "front view", "back view" (or similar).
One advantage is that you can easily update your images in the future and another is that SharePoint automatically creates two preview miniatures in convenient sizes for your images which allows you to reduce bandwidth.

Categories