I currently want to download all email messages (regardless in which folder they're in) to my SQL Server database.
Now while I know how to search for email messages or subscribe to streaming notifications, I've yet to learn on how to synchronize all messages from EWS to my database.
var emailMessages = GetItems<MSEmailMessage>(WellKnownFolderName.MsgFolderRoot);
foreach (var emailMessage in emailMessages)
{
Debug.WriteLine(emailMessage.Subject);
}
private IList<T> GetItems<T>(WellKnownFolderName wellKnownFolderName) where T : Item
{
IList<T> result = new List<T>();
Folder folder = Folder.Bind(_exchangeService, wellKnownFolderName);
if (folder.TotalCount > 0)
{
ItemView view = new ItemView(folder.TotalCount);
FindItemsResults<Item> items = _exchangeService.FindItems(wellKnownFolderName, view);
foreach (var resultItem in items.OfType<T>())
{
result.Add(resultItem);
}
}
return result;
}
This returns 0 email messages (it even threw an exception before checking for the folder.TotalCount before initializing a new ItemView...).
While checking for WellKnownFolderName.Inbox returns the email messages from the inbox, it does not allow me to query for sub folders to synchronize the entirety of the messages.
What am I missing?
You can build up a list of folders to search for mail in. Then iterate through each folder and get all the emails in that folder.
In the code snippet below we can create a folderSearchFilter with FolderTraversal set to Deep which will scan all sub folders of the target folder. We can then apply this filter to the two main well-known folders Inbox and SentItems
Once you have a list of folders to index, then you can use your own code to retrieve all the mails from that folder.
var view = new FolderView(int.MaxValue)
{
PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) { FolderSchema.DisplayName }
};
SearchFilter foldersearchFilter = new SearchFilter.IsGreaterThan(FolderSchema.TotalCount, 0);
view.Traversal = FolderTraversal.Deep;
List<Folder> searchFolders;
try
{
searchFolders = new List<Folder>
{
Folder.Bind(ExchangeService, WellKnownFolderName.Inbox),
Folder.Bind(ExchangeService, WellKnownFolderName.SentItems)
};
}
catch (ServiceResponseException e) {}
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.Inbox, foldersearchFilter, view).Folders);
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.SentItems, foldersearchFilter, view).Folders);
var results = new List<Item>();
foreach (var searchFolder in searchFolders)
{
//Get all emails in this folder
}
Right, the root folder likely has 0 messages in it. When you do FindItems in a folder, the results don't bubble up from subfolders. You need to iterate over every folder if you want to get to their messages.
Related
I'm very new to C# development.
I'm trying to check and create a folder/ sub-folder exist in Outlook Mailbox using Exchange Web Service.
Folder Structure
MAIN_folder
Sub Folder-1
Sub Folder-2
Sub Folder-3
Implementation
public void checkFolderExistOrNot( String folder_name)
{
FolderView fv = new FolderView(100);
var findFoldersResults = exchangeService.FindFolders(
WellKnownFolderName.Inbox,
new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
new SearchFilter.ContainsSubstring(FolderSchema.DisplayName, folder_name)),
fv);
foreach (var folder in findFoldersResults)
{
if (folder is Folder)
{
if (folder.DisplayName == folder_name)
{
archiveFolderID = folder.Id;
}
}
}
//if archive folder not found create and assign the variable to the folderID
if (archiveFolderID == null)
{
Folder folder = new Folder(exchangeService);
folder.DisplayName = folder_name;
folder.Save(WellKnownFolderName.Inbox);
archiveFolderID = folder.Id;
}
}
checkFolderExistOrNot(MAIN_folder)
checkFolderExistOrNot(MAIN_folder.Sub Folder-1)
checkFolderExistOrNot(MAIN_folder.Sub Folder-2)
checkFolderExistOrNot(MAIN_folder.Sub Folder-3)
But this is only creating the Main folder under the inbox. It would be greatly appreciated if someone could help me to identify what is missing in my implementation.
Thanks in Advance.
The only way to tell if a folder exists is to search for it with your search because you don't specify the traversal in the Folderview it will always be shallow. If you specify a deep traversal in
FolderView fv = new FolderView(100);
fv.Traversal = FolderTraversal.Deep;
You should then be able to find the parent folder you want to create a new subfolder on. Your logic should work okay as long as you don't have any name clashes a different folder levels. Otherwise what I do is this Exchange Web Service FolderId for a folder created by user or Get to an Exchange folder by path using EWS
Have you given Microsoft Graph a look?
You can basically use it for anything in Microsoft 365. With you you can also achieve your goal.
You will need to create a GraphServiceClient and with it you can do the following to check if a folder exists:
string user = "emailAddressOfTheUser";
var parentFolderRequest = graphClient.Users[user].MailFolders.Inbox.ChildFolders
.Request()
.Filter($"startsWith(displayName, 'parentFolderName')");
var parentMailFolder = await parentFolderRequest.GetAsync(cancellationToken);
Once you have the parent folder you can get it's ID and once you know that you can search it for child folders:
var parentMailFolderID = parentMailFolder.First().Id;
var childFolderRequest = graphClient.Users[user].MailFolders[parentMailFolderID].ChildFolders
.Request()
.Filter($"startsWith(displayName, 'childFolderName')");
var childMailFolder = await parentFolderRequest.GetAsync(cancellationToken);
If the childMailFolder.Count > 0 then the folder exists, if not you create the child folder:
var childFolder = new MailFolder
{
DisplayName = "childFolderName",
IsHidden = false
};
await graphClient.Users[graphUser.Id]
.MailFolders[parentMailFolderID].ChildFolders
.Request()
.AddAsync(childFolder );
I am trying to get the sent folders to display but it shows that the folder has no children in it. All folders are empty except inbox. I am using the following code.
using (var client = new ImapClient())
{
client.Connect(credentials.incoming_host, (int)credentials.incoming_port, credentials.incoming_ssl); //for SSL
client.Authenticate(credentials.email, credentials.password);
client.Inbox.Open(FolderAccess.ReadOnly);
var sentFolder= client.GetFolder(MailKit.SpecialFolder.Sent);
var Folders = client.GetFolders(client.PersonalNamespaces[0]);
client.Disconnect(true);
}
I tried sending an email using the same folder, and then append it like:
var sentFolder = imapclient.GetFolder(SpecialFolder.Sent);
sentFolder.Append(message);
My outlook did detect it and added into the sent folder.
From the MailKit README:
If the IMAP server supports the SPECIAL-USE or the XLIST (GMail) extension, you can get ahold of the pre-defined All, Drafts, Flagged (aka Important), Junk, Sent, Trash, etc folders like this:
if ((client.Capabilities & (ImapCapabilities.SpecialUse | ImapCapabilities.XList)) != 0) {
var drafts = client.GetFolder (SpecialFolder.Drafts);
} else {
// maybe check the user's preferences for the Drafts folder?
}
In cases where the IMAP server does not support the SPECIAL-USE or XLIST extensions, you'll have to come up with your own heuristics for getting the Sent, Drafts, Trash, etc folders. For example, you might use logic similar to this:
static string[] CommonSentFolderNames = { "Sent Items", "Sent Mail", "Sent Messages", /* maybe add some translated names */ };
static IFolder GetSentFolder (ImapClient client, CancellationToken cancellationToken)
{
var personal = client.GetFolder (client.PersonalNamespaces[0]);
foreach (var folder in personal.GetSubfolders (false, cancellationToken)) {
foreach (var name in CommonSentFolderNames) {
if (folder.Name == name)
return folder;
}
}
return null;
}
Using LINQ, you could simplify this down to something more like this:
static string[] CommonSentFolderNames = { "Sent Items", "Sent Mail", "Sent Messages", /* maybe add some translated names */ };
static IFolder GetSentFolder (ImapClient client, CancellationToken cancellationToken)
{
var personal = client.GetFolder (client.PersonalNamespaces[0]);
return personal.GetSubfolders (false, cancellationToken).FirstOrDefault (x => CommonSentFolderNames.Contains (x.Name));
}
Another option might be to allow the user of your application to configure which folder he or she wants to use as their Sent folder, Drafts folder, Trash folder, etc.
How you handle this is up to you.
It is necessary to open the folder otherwise it will appear empty.
IMailFolder personal = client.GetFolder(client.PersonalNamespaces[0]);
foreach (IMailFolder folder in personal.GetSubfolders(false, cancellationToken))
{
folder.Open(FolderAccess.ReadOnly);
Console.WriteLine($"folder.Name = {folder.Name}");
Console.WriteLine($"{folder.Name} messages : {folder.Count}");
}
I've got a strange problem with EWS. I am writing a console app which queries my inbox for messages with a subject containing 'Ref:' which is absolutely fine, I get a connection no problem, run the query, get some results. but strangely it is only returning messages that are from internal senders, any messages sent from outside the exchange organisation do not appear in the results.
ultimately I am going to be using a service account to connect to various mailboxes and find various emails with certain references in the subject....
Does anyone have any idea what might be going on? I've gone over it and over it and can't find any reason why it's doing it.
It's Exchange 2010 SP2
ExchangeService exchService = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
exchService.Url = new System.Uri("https://xxx.xxxxxxxxx.co.uk/EWS/Exchange.asmx");
exchService.UseDefaultCredentials = true;
var userMailbox = new Mailbox("xxxx#xxxx.co.uk");
var inboxFolder = new FolderId(WellKnownFolderName.Inbox, userMailbox);
String qString = "subject:\"Ref:\"";
ItemView view = new ItemView(20);
view.PropertySet = new PropertySet(ItemSchema.Id);
try
{
FindItemsResults<Item> results = exchService.FindItems(inboxFolder, qString, view);
if (results.Items.Count > 0)
{
foreach (Item item in results.Items)
{
Console.WriteLine(item.Id);
if (item is EmailMessage)
{
EmailMessage bindMessage = EmailMessage.Bind(exchService, item.Id.ToString());
String sender = bindMessage.Sender.Address.ToString();
String Body = bindMessage.Body.Text.ToString();
Console.WriteLine("Sender: " + sender);
}
}
}
}
I can't seem to get this working. I am trying to iterate through all the mail items in a folder I created named 'SlaughterPDFs' and delete the emails.
Below is the code I am using. In this code I was just trying to delete the mail items out of the outlook 'Drafts' folder.
public void deleteMails()
{
Application tempApp = new Application();
MAPIFolder tempInbox = default(MAPIFolder);
Items JunkItems = default(Items);
tempInbox = tempApp.GetNamespace("MAPI").
GetDefaultFolder(OlDefaultFolders.olFolderDrafts);
JunkItems = tempInbox.Items;
MailItem DeleteMail = default(MailItem);
foreach (object newMail_loopVariable in JunkItems)
{
DeleteMail = (MailItem)newMail_loopVariable;
DeleteMail.Delete();
}
JunkItems = null;
tempInbox = null;
tempApp = null;
}
Anyone have any idea what I am doing wrong? Or should I be moving these emails to a new folder.
I'm not sure where you are having an issue.
If you replace your for loop with the below while loop it should delete all emails in the folder.
while (tempInbox.Items.Count > 0)
{
DeleteMail = (MailItem)tempInbox.Items.GetFirst();
DeleteMail.Delete();
}
If you are having trouble accessing the folder I would use (assuming SlaughterPDFs is a sub folder of inbox):
tempInbox = tempApp.GetNamespace("MAPI").
GetDefaultFolder(OlDefaultFolders.olFolderInbox);
tempInbox = tempInbox.Folders["SlaughterPDFs"];
I am using EWS and wish to obtain the conversation history folder from Office365 Exchange Online.
It was possible to achieve it by the method of judging the folder with DisplayName by the acquisition of the child folder from the root folder.
However, the user can change the name to this folder.
Is there a method that judges from ExtendedProperty to solve this problem?
Moreover, is there an another way?
-code sample-
Folder folder = Folder.Bind(this._exchange, WellKnownFolderName.Root);
//acquire the total number of cases including the subfolder.
FolderView view = new FolderView(1);
view.Traversal = FolderTraversal.Deep;
FindFoldersResults result = folder.FindFolders(view);
//acquire All folders.
view.PageSize = result.TotalCount;
result = folder.FindFolders(view);
FindFoldersResults folders = folder.FindFolders(view);
foreach (var f in folders.Folders)
{
//I want to judge the history folder of the conversation here excluding the DisplayName property.
}
Please anyone provide a good idea ,samples.
regards.
To check if folder contains items from some conversation you can use something like that:
var filter = new SearchFilter.IsEqualTo(EmailMessageSchema.ConversationId, convId);
var itemView = new ItemView();
foreach (var f in folders.Folders)
{
var findResult = f.FindItems(filter, itemView);
}
Filter should restrict find results only to items that has property ConversationId equal to conversation you are interested in.