How can I use ExchangeService to access a shared mailbox (Outlook 2013) - c#

I am trying to access a second mailbox in outlook using an ExchangeService. but when I run the application it grabs my main email. Its worth noting that I use windows authentication here, and that I need to for this to work.
var useExchangeServer = new UseExchangeServer("SECONDEMAIL#mycompany.com");
var messages = useExchangeServer.LoadMessages(1);
foreach (var message in messages){ //At this point message has the wrong email...
...
}
here is my "UseExchangeServer" class
public UseExchangeServer(string mailBox)
{
_service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
_service.UseDefaultCredentials = true;
_service.AutodiscoverUrl(mailBox);
}
public FindItemsResults<Item> GetLastItems(int numberOfItems)
{
return _service.FindItems(WellKnownFolderName.Inbox, new ItemView(numberOfItems));
}
public IEnumerable<EmailMessage> LoadMessages(int numberOfMessages)
{
var findResults = GetLastItems(numberOfMessages);
foreach (var item in findResults.Items)
{
var message = EmailMessage.Bind(_service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));
message.Load();
yield return message;
}
}

You need to use the FolderId overload to specify the Mailbox you want to access otherwise the Mailbox that belongs to the credentials you are using will be used so you will need to do something like the following with your code
public UseExchangeServer(string mailBox)
{
_service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
_service.UseDefaultCredentials = true;
_service.AutodiscoverUrl(mailBox);
}
public FindItemsResults<Item> GetLastItems(int numberOfItems,string mailBox)
{
FolderId FolderToAccess = new FolderId(WellKnownFolderName.Inbox,mailBox);
return _service.FindItems(FolderToAccess, new ItemView(numberOfItems));
}
public IEnumerable<EmailMessage> LoadMessages(int numberOfMessages)
{
var findResults = GetLastItems(numberOfMessages);
foreach (var item in findResults.Items)
{
var message = EmailMessage.Bind(_service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));
message.Load();
yield return message;
}
}
Cheers
Glen

Related

Chain Email Display Broken by Updating Subject Exchange Online Service in C#

I have read emails from exchnage online and in convesation emails i want to show only last email from its chain messages i have successfully doing that but in desktop outlook application i changed the subject then the Convesation chain mail is not working properly and chain is broken.
First I Have Group by ConvesationId.Then In-reply-to check for mail is convesation email or not.If Convesation email then find it in other way and simple mail then find it another way
It's ConvesationId And ConvesationIndex also changed.
public async Task<IActionResult> TrackEmail()
{
var cca = ConfidentialClientApplicationBuilder
.Create(Clientid)
.WithClientSecret(Clientsecret)
.WithTenantId(Tenantid)
.Build();
var scopes = new string[] { $"{_emailConfiguration.ScopeUrl}" };
var aquireToken = await cca.AcquireTokenForClient(scopes).ExecuteAsync();
aquireToken.ExpiresOn.UtcDateTime.AddMinutes(-5);
ExchangeService ewsClient = new(ExchangeVersion.Exchange2013_SP1);
ewsClient.Url = new Uri($"{_emailConfiguration.ExchangeUrl}");
ewsClient.Credentials = new OAuthCredentials(aquireToken.AccessToken);
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, $"{email}");
ewsClient.TraceEnabled = false;
ewsClient.TraceFlags = TraceFlags.None;
ewsClient.Timeout = 9000000;
if (ewsClient != null)
{
List<ProjectsDataModel>? projectsDataModel = (from p in _db.Projects
where p.Userid== userId
select new ProjectsDataModel
{
Id = p.Id,
ProjectName = p.Projectname,
ProjectDomain = p.Projectdomain,
CreatedDate = p.Createddate
}).ToList();
if (projectsDataModel != null)
{
foreach (var projectData in projectsDataModel)
{
EmailAddress sender = new(projectData.ProjectDomain);
ItemView view = new ItemView(int.MaxValue);
//SearchFilter.IsEqualTo filter = new SearchFilter.IsEqualTo(EmailMessageSchema.Sender, sender);
ExtendedPropertyDefinition PidTagSenderSmtpAddress = new(0x5D01, MapiPropertyType.String);
SearchFilter sf = new SearchFilter.ContainsSubstring(PidTagSenderSmtpAddress, $"{projectData.ProjectDomain}");
var folderItems = ewsClient.FindItems(WellKnownFolderName.Inbox, sf, view);
var conversationItems = from element in folderItems
group element by element.ConversationId
into groups
select groups.OrderByDescending(p => p.DateTimeReceived).FirstOrDefault();
foreach (Item item in conversationItems)
{
EmailMessage messageToCheck = EmailMessage.Bind(ewsClient, item.Id);
if (messageToCheck.InReplyTo != null) //Chain Email or not
{
PropertySet properties = new(BasePropertySet.FirstClassProperties);
// Identify the folders to ignore.
Collection<FolderId> foldersToIgnore = new Collection<FolderId>()
{ WellKnownFolderName.DeletedItems, WellKnownFolderName.Drafts };
// Request the conversation items.
ConversationResponse convesationResponse = ewsClient.GetConversationItems(messageToCheck.ConversationId,
properties,
null,
foldersToIgnore,
ConversationSortOrder.TreeOrderDescending);
//foreach (ConversationNode node in convesationResponse.ConversationNodes)
//{
// // Process each item in the conversation node.
// foreach (Item chainItem in node.Items)
// {
var chainItem = convesationResponse.ConversationNodes.FirstOrDefault()?.Items.FirstOrDefault();
if (chainItem != null)
{
List<EmailInfo> emailInfosList = projectData.EmailInfos.Where(p => p.ConversationId == chainItem.ConversationId).ToList();
if (emailInfosList == null || emailInfosList.Count == 0)
{
EmailMessage chainMessage = EmailMessage.Bind(ewsClient, chainItem.Id);
EmailInfo emailInfo = new();
emailInfo.Subject = chainMessage.Subject;
emailInfo.From = chainMessage.From.ToString();
emailInfo.Body = chainMessage.Body.ToString();
emailInfo.DateTimeCreated = chainMessage.DateTimeCreated;
emailInfo.DateTimeSent = chainMessage.DateTimeSent;
emailInfo.DateTimeReceived = chainMessage.DateTimeReceived;
emailInfo.ConversationId = chainMessage.ConversationId;
projectData.EmailInfos.Add(emailInfo);
}
}
// }
//}
}
else
{
List<EmailInfo> emailInfosList = projectData.EmailInfos.Where(p => p.ConversationId == messageToCheck.ConversationId).ToList();
if (emailInfosList == null || emailInfosList.Count == 0)
{
EmailInfo emailInfo = new();
emailInfo.Subject = messageToCheck.Subject;
emailInfo.From = messageToCheck.From.ToString();
emailInfo.Body = messageToCheck.Body.ToString();
emailInfo.DateTimeCreated = messageToCheck.DateTimeCreated;
emailInfo.DateTimeSent = messageToCheck.DateTimeSent;
emailInfo.DateTimeReceived = messageToCheck.DateTimeReceived;
emailInfo.ConversationId = messageToCheck.ConversationId;
projectData.EmailInfos.Add(emailInfo);
}
}
}
response.Data = projectsDataModel;
}
}
else
{
response.StatusCode = (int)HttpStatusCode.NotFound;
response.Message = ATEmailClientLibrary.Models.ResponseMessage.NoRecordFound;
}
}
else
{
response.StatusCode = (int)HttpStatusCode.NotFound;
response.Message = ATEmailClientLibrary.Models.ResponseMessage.NoRecordFound;
}
}
Why this email chain is broken i want to get only last email from this chain email messages but don't know how to do when subject change then this chain message broken

how to have a event listener to office 365 when a new mail is received

I have migrated the mailbox from exchange server to office 365.
I have already written the code to connect to office 365 using the credentials and so i am able to read all the email that are there in the inbox.Please find the below code
public async System.Threading.Tasks.Task test()
{
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = ConfigurationManager.AppSettings["appId"],
TenantId = ConfigurationManager.AppSettings["tenantId"],
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
var ewsScopes = new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" };
try
{
string password = "test";
SecureString sec_pass = new SecureString();
Array.ForEach(password.ToArray(), sec_pass.AppendChar);
sec_pass.MakeReadOnly();
// Make the interactive token request
var authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, "test#demotenant.com", sec_pass).ExecuteAsync();
//var authResult = await pca.AcquireTokenInteractive(ewsScopes).ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
//ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "test#demotenant.onmicrosoft.com");
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
FindItemsResults<Item> result = ewsClient.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
foreach (Item item in result)
{
EmailMessage message = EmailMessage.Bind(ewsClient, item.Id);
String body = message.ConversationTopic;
String from = message.From.Address.ToString();
}
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex.ToString()}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.ToString()}");
}
}
Now i am looking to add a listener which can run this code whenever a new mail is received in the inbox.
Can someone suggest me on how i can do this.
EWS Streaming or push notifications would be one way to do this in EWS https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/notification-subscriptions-mailbox-events-and-ews-in-exchange. A better approach would be to use Webooks in the Graph API and don't use EWS https://learn.microsoft.com/en-us/graph/api/resources/webhooks?view=graph-rest-1.0 (unless you need you code to run OnPrem). The other thing I would suggest is look at Power Automate (previously Flow) which also has the ability to trigger a large number actions on new Mail receipt.

EWS can't get all unreaded mail which is got error while reading

I have windows service that working every 10 second. It's working fine generally but sometimes im getting these errors;
at Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest1.Execute()
at OutlookService.ItemLoad(EmailMessage message, List1& attachmentList) in \OutlookService.cs:line 69; The request failed. Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
25.09.2019 15:03:53: Microsoft.Exchange.WebServices:at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponse(IEwsHttpWebResponse response)
My problem is if that mail got error while reading it cannot be read again. Its stay unread mail but i cannot get it.
My Code;
private static ExchangeService Service2013
{
get
{
if (_service2013 == null)
{
var uname = "mail";
var pwd = "pass";
_service2013 = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
_service2013.Credentials = new WebCredentials(uname, pwd);
_service2013.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
}
return _service2013;
}
}
ExchangeService service = OutlookService.Service;
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
try
{
FindItemsResults<Item> findResults;
ItemView view = new ItemView(100);
do
{
findResults = service.FindItems(WellKnownFolderName.Inbox, sf, view);
if (findResults.Items.Count > 0)
{
foreach (var item in findResults.Items)
{
item.Load();
var emailMessage = item as EmailMessage;
if (!string.IsNullOrEmpty(emailMessage.Subject))
{
if (!emailMessage.Subject.ToLower().Contains("automatic reply") && !emailMessage.Subject.ToLower().Contains("otomatik yanıt"))
{
ReadMail(item as EmailMessage);
GC.SuppressFinalize(item);
}
else
{
emailMessage.IsRead = true;
emailMessage.Update(ConflictResolutionMode.AlwaysOverwrite);
}
}
else
{
ReadMail(item as EmailMessage);
GC.SuppressFinalize(item);
}
}
}
view.Offset = findResults.NextPageOffset.GetValueOrDefault(0);
GC.SuppressFinalize(findResults);
} while (findResults.MoreAvailable);
GC.SuppressFinalize(view);
}
catch (Exception ex)
{
//Debug.WriteLine(string.Format("Operate Error {0}.", ex.Message));
Logger.WriteLog(ex);
}

C# EWS Save all Email Found in Inbox Into an Item List

My current code accesses all unread email in Inbox. However, as this is a method which will return the list of emails for further processing for each email.
I am unsure of how to group the emails found into a list or table.
Any advice will be greatly appreciated.
private static GetNewEmailInInbox(ExchangeService service, int batch, string autoDiscoverURL)
{
if (service != null)
{
Console.WriteLine("Accessing system account mailbox...");
TimeSpan ts = new TimeSpan(0, -1, 0, 0);
DateTime date = DateTime.Now.Add(ts);
service.AutodiscoverUrl(autoDiscoverURL);
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
FindItemsResults<Item> emailItemList;
ItemView view = new ItemView(50);
int inboxCount = 1;
do
{
emailItemList = service.FindItems(WellKnownFolderName.Inbox, sf, view);
foreach (var emailItem in emailItemList.Items)
{
Console.WriteLine(inboxCount + ". " + emailItem.Subject);
inboxCount++;
//add this email to an allEmailList
}
if (!emailItemList.NextPageOffset.HasValue)
break;
}
while (emailItemList.MoreAvailable);
}
return allEmailList;
}
You can group the emails using an IList Interface.
IList<T> allEmailList = new List<T>();
FindItemsResults<Item> emailItemList = service.FindItems(WellKnownFolderName.Inbox, sf, view);
foreach (var emailItem in emailItemList.Items.OfType<T>())
{
Console.WriteLine(inboxCount + ". " + emailItem.Subject);
inboxCount++;
//add this email to an allEmailList
allEmailList.Add(emailItem);
}

MailItem attachment unknown

I have a mail containing only a signature as an image and an attachment like the screenshot below.
I save this email as C:\mail.msg, I try then to read it by the code below:
var oApp = new Microsoft.Office.Interop.Outlook.Application();
MailItem outlookMsg = (Microsoft.Office.Interop.Outlook.MailItem)oApp.CreateItemFromTemplate(#"C:\mail.msg");
//there are 2 attachments inside
foreach(var att in outlookMsg.Attachments)
{
att.SaveAsFile($#"C:\{att.FileName}");
}
The problem
There are 2 attachments inside the MailItem named :
-empty.xlsx
-lot4.xlsx
If I change the extension of lot4.xlsx to lot4.png, it can be opened as the image in the signature.
Someone has seen this strange situation when an attachment is added with name incorrect?
You could download attachments using the below code:
private void ThisApplication_NewMail()
{
Outlook.MAPIFolder inBox = this.Application.ActiveExplorer()
.Session.GetDefaultFolder(Outlook
.OlDefaultFolders.olFolderInbox);
Outlook.Items inBoxItems = inBox.Items;
Outlook.MailItem newEmail = null;
inBoxItems = inBoxItems.Restrict("[Unread] = true");
try
{
foreach (object collectionItem in inBoxItems)
{
newEmail = collectionItem as Outlook.MailItem;
if (newEmail != null)
{
if (newEmail.Attachments.Count > 0)
{
for (int i = 1; i <= newEmail
.Attachments.Count; i++)
{
newEmail.Attachments[i].SaveAsFile
(#"C:\TestFileSave\" +
newEmail.Attachments[i].FileName);
}
}
}
}
}
catch (Exception ex)
{
string errorInfo = (string)ex.Message
.Substring(0, 11);
if (errorInfo == "Cannot save")
{
MessageBox.Show(#"Create Folder C:\TestFileSave");
}
}
}
For more information, please refer to this link:
How to: Programmatically save attachments from Outlook email items
With Microsoft EWS, its very easy to do:
reference: http://johnlabtest.blogspot.com/2014/01/save-attachments-from-exchange-mail-box.html
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials("user1#contoso.com", "password");
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.AutodiscoverUrl("user1#contoso.com", RedirectionUrlValidationCallback);
var messages = new List<EmailMessage>();
// only get unread emails
SearchFilter folderSearchFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false);
// we just need the id in our results
var itemView = new ItemView(10) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
FindItemsResults<Item> findResults = service.FindItems(folder.Id, folderSearchFilter, itemView);
foreach (Item item in findResults.Items.Where(i => i is EmailMessage))
{
EmailMessage message = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments, ItemSchema.HasAttachments));
messages.Add(message);
}
// loop through messages and call processemail here.
}
public static void ProcessEmail(EmailMessage message)
{
string saveDir = ConfigurationManager.AppSettings["AttachmentSaveDirectory"];
if (message.HasAttachments)
{
foreach (Attachment attachment in message.Attachments.Where(a=> a is FileAttachment))
{
FileAttachment fileAttachment = attachment as FileAttachment;
fileAttachment.Load(); // populate the content property of the attachment
using (FileStream fs = new FileStream(saveDir + attachment.Name, FileMode.Create))
{
using (BinaryWriter w = new BinaryWriter(fs))
{
w.Write(fileAttachment.Content);
}
}
}
}
message.IsRead = true;
message.Update(ConflictResolutionMode.AutoResolve); // push changes back to server
}
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;
}

Categories