Cross folder searching of Exchange using Exchange Web Services - exchange-server

Is there a way to search Exchange using the EWS Managed API for all email messages across ALL folders. I'm using the FindItems API call -- but that appears to require that the search be confined to a single folder.
private void InternalPurgeProcessFolder(FolderId folderId, ExchangeService service, SearchFilter searchCriteria) {
Logger.Info("Processing folder {0}", folderId.FolderName);
int pageSize = _runtimeParameters.ExchangeRetrievalPageSize;
ItemView itemView = new ItemView(pageSize);
itemView.PropertySet = Utils.BasicPropertySet();
const int maxInterationsForTesting = 2;
int iterations = 0;
FindItemsResults<Item> findResults;
do {
++iterations;
Logger.Debug("Start of iteration {0}", iterations);
findResults = service.FindItems(folderId, searchCriteria, itemView);
_dumper.ListDatesAndSubjectsBrief(findResults);
itemView.Offset += pageSize;
} while ((findResults.MoreAvailable) && (iterations < maxInterationsForTesting));
}

To find items across folders you can create a search folder and in the SearchFolderParameters set the RootFolderIds to the root folder of the mailbox and the traversal mode to SearchFolderTraversal.Deep.

Related

Use Redemption and Profman to add Exchange Online mailbox to existing profile

I'm trying to use Redemption and Profman to add an Exchange Online mailbox to an existing Outlook profile. I want to simulate the same result as when you use File | Add Account in Outlook. (I don't want to add a delegate mailbox.)
I've written some code (it's below), but I can't get it to work. I am testing with an email account that I have no problem adding manually. When I use the File | Add Account in Outlook, a screen pops up that asks me for an email address. I enter the email address, and the account is successfully added to my profile. I don't even need to enter a password.
However, when I use the code below for the same email address, it doesn't work. The mailbox shows up in Outlook, but when I click on it, I get the message: "The set of folders cannot be opened. The attempt to log on to Microsoft Exchange has failed". And there is no prompt for me to enter a password.
I'm using Outlook 2021. The code below was adapted from:
https://www.dimastr.com/redemption/profman_examples.htm#ROH_Profile_Outlook2016
private static void AddExoMailbox(string profileName, string email)
{
const int PR_DISPLAY_NAME_W = 0x3001001F;
const int PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W = 0x6641001F;
const int PR_EMSMDB_SECTION_UID = 0x3D150102;
const int PR_STORE_PROVIDERS = 0x3D000102;
var profile = new ProfMan.Profiles().Item[profileName];
var ExchService = profile.Services.Add("MSEMS", "Microsoft Exchange", false);
var vProfileGuid = ExchService.ProfSect.Item[PR_EMSMDB_SECTION_UID];
dynamic ProviderStoreUid = null;
for (int k = 1; k <= ExchService.Providers.Count; k++)
{
var ExProvider = ExchService.Providers.Item[k];
var ProviderUid = ExProvider.UID;
if (ProviderUid == vProfileGuid)
{
var ProviderProfSect = ExProvider.ProfSect;
ProviderProfSect.Item[PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W] = $"SMTP:{email}";
ProviderStoreUid = ExchService.ProfSect.Item[PR_STORE_PROVIDERS];
}
}
for (int k = 1; k <= ExchService.Providers.Count; k++)
{
var ExProvider = ExchService.Providers.Item[k];
var ProviderUid = ExProvider.UID;
if (ProviderUid == ProviderStoreUid)
{
var StoreProviderProfSect = ExProvider.ProfSect;
StoreProviderProfSect.Item[PR_DISPLAY_NAME_W] = email;
StoreProviderProfSect.Item[PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W] = $"SMTP:{email}";
}
}
}
Any pointers?

get calendarrules by servicenameid

In Dynamics 365 (on premise) I need to delete Service Restrictions, Associated with a specific service. It looks like they are stored in "calendarrules", but when i try to find some info in my collection of calendarrules, I do not see any attributes similar to "ServiceNameId".
I found the information that this can be done simply by changing the entries of the calendar rule and updating the original calendar, so I try to do it as follows, but can't see attributes referring me to services.
I get the collection as follows:
SystemUser user = service.Retrieve(SystemUser.EntityLogicalName, UserId, new ColumnSet("calendarid")).ToEntity<SystemUser>();
Calendar userCalendar = service.Retrieve(Calendar.EntityLogicalName, user.CalendarId.Id, new ColumnSet(true)).ToEntity<Calendar>();
EntityCollection entityCollection = (EntityCollection)userCalendar.Attributes["calendarrules"];
Ok, it can be found on innercalendars and updated by this way:
SystemUser user = service.Retrieve(SystemUser.EntityLogicalName, serviceappointment.OwnerId.Id, new ColumnSet("calendarid")).ToEntity<SystemUser>();
Calendar userCalendar = service.Retrieve(Calendar.EntityLogicalName, user.CalendarId.Id, new ColumnSet(true)).ToEntity<Calendar>();
EntityCollection entityCollection = (EntityCollection)userCalendar.Attributes["calendarrules"];
foreach (Entity ent in entityCollection.Entities)
{
int num = 0;
Entity newEntity = service.Retrieve("calendar", ((EntityReference)ent.Attributes["innercalendarid"]).Id, new ColumnSet(true));
EntityCollection calendarRules = (EntityCollection)newEntity.Attributes["calendarrules"];
List<int> list = new List<int>();
foreach (CalendarRule cr in calendarRules.Entities)
{
if (cr.ServiceId != null)
{
list.Add(num);
}
num++;
}
for (int i = 0; i < list.Count; i++)
{
calendarRules.Entities.Remove(calendarRules.Entities[list[i]]);
}
newEntity.Attributes["calendarrules"] = calendarRules;
service.Update(newEntity);
}
Perhaps there is a simpler way, but for now this is enough

How to retrieve all contacts from Microsoft Exchange using EWS Managed API?

all I need to do is to retrieve all contacts from Microsoft Exchange. I did some research and the best possible option for me should be to use EWS Managed API. I am using Visual Studio and C# programming leanguage.
I think I am able to connect to Office365 account, because I am able to send a message in a program to specific email. But I am not able to retrieve the contacts.
If I create a contact directly in source code, the contact will be created in Office365->People aplication. But I don't know why! I thought I was working with Exchange aplication.
Summarize:
Is there any possibility how to get all contacts from Office365->Admin->Exchange->Acceptencers->Contacts ?
Here is my code:
class Program
{
static void Main(string[] args)
{
// connecting to my Exchange account
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials("office365email#test.com", "password123");
/*
// debugging
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
*/
service.AutodiscoverUrl("office365email#test.com", RedirectionUrlValidationCallback);
// send a message works good
EmailMessage email = new EmailMessage(service);
email.ToRecipients.Add("matoskok1#gmail.com");
email.Subject = "HelloWorld";
email.Body = new MessageBody("Toto je testovaci mail");
email.Send();
// Create the contact creates a contact in Office365 -> People application..Don't know why there and not in Office365 -> Exchange
/*Contact contact = new Contact(service);
// Specify the name and how the contact should be filed.
contact.GivenName = "Brian";
contact.MiddleName = "David";
contact.Surname = "Johnson";
contact.FileAsMapping = FileAsMapping.SurnameCommaGivenName;
// Specify the company name.
contact.CompanyName = "Contoso";
// Specify the business, home, and car phone numbers.
contact.PhoneNumbers[PhoneNumberKey.BusinessPhone] = "425-555-0110";
contact.PhoneNumbers[PhoneNumberKey.HomePhone] = "425-555-0120";
contact.PhoneNumbers[PhoneNumberKey.CarPhone] = "425-555-0130";
// Specify two email addresses.
contact.EmailAddresses[EmailAddressKey.EmailAddress1] = new EmailAddress("brian_1#contoso.com");
contact.EmailAddresses[EmailAddressKey.EmailAddress2] = new EmailAddress("brian_2#contoso.com");
// Specify two IM addresses.
contact.ImAddresses[ImAddressKey.ImAddress1] = "brianIM1#contoso.com";
contact.ImAddresses[ImAddressKey.ImAddress2] = " brianIM2#contoso.com";
// Specify the home address.
PhysicalAddressEntry paEntry1 = new PhysicalAddressEntry();
paEntry1.Street = "123 Main Street";
paEntry1.City = "Seattle";
paEntry1.State = "WA";
paEntry1.PostalCode = "11111";
paEntry1.CountryOrRegion = "United States";
contact.PhysicalAddresses[PhysicalAddressKey.Home] = paEntry1;
// Specify the business address.
PhysicalAddressEntry paEntry2 = new PhysicalAddressEntry();
paEntry2.Street = "456 Corp Avenue";
paEntry2.City = "Seattle";
paEntry2.State = "WA";
paEntry2.PostalCode = "11111";
paEntry2.CountryOrRegion = "United States";
contact.PhysicalAddresses[PhysicalAddressKey.Business] = paEntry2;
// Save the contact.
contact.Save();
*/
// msdn.microsoft.com/en-us/library/office/jj220498(v=exchg.80).aspx
// Get the number of items in the Contacts folder.
ContactsFolder contactsfolder = ContactsFolder.Bind(service, WellKnownFolderName.Contacts);
Console.WriteLine(contactsfolder.TotalCount);
// Set the number of items to the number of items in the Contacts folder or 50, whichever is smaller.
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(numItems);
// To keep the request smaller, request only the display name property.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName);
// Retrieve the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
// Display the list of contacts.
foreach (Item item in contactItems)
{
if (item is Contact)
{
Contact contact1 = item as Contact;
Console.WriteLine(contact1.DisplayName);
}
}
Console.ReadLine();
} // end of Main() method
/*===========================================================================================================*/
private static bool CertificateValidationCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// If the certificate is a valid, signed certificate, return true.
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
{
return true;
}
// If there are errors in the certificate chain, look at each error to determine the cause.
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
if (chain != null && chain.ChainStatus != null)
{
foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
{
if ((certificate.Subject == certificate.Issuer) &&
(status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
{
// Self-signed certificates with an untrusted root are valid.
continue;
}
else
{
if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
{
// If there are any other errors in the certificate chain, the certificate is invalid,
// so the method returns false.
return false;
}
}
}
}
// When processing reaches this line, the only errors in the certificate chain are
// untrusted root errors for self-signed certificates. These certificates are valid
// for default Exchange server installations, so return true.
return true;
}
else
{
// In all other cases, return false.
return false;
}
}
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;
}
}
seems like you are already doing a good job doing a FindItems() method.
In order to get all contacts all you need to add is a SearchFilter, in this case I'd recommend doing the Exists() filter as you can simply say Find All contacts with an id.
See my example below if you need an example of a Full Windows Application dealing with contacts see my github page github.com/rojobo
Dim oFilter As New SearchFilter.Exists(ItemSchema.Id)
Dim oResults As FindItemsResults(Of Item) = Nothing
Dim oContact As Contact
Dim blnMoreAvailable As Boolean = True
Dim intSearchOffset As Integer = 0
Dim oView As New ItemView(conMaxChangesReturned, intSearchOffset, OffsetBasePoint.Beginning)
oView.PropertySet = BasePropertySet.IdOnly
Do While blnMoreAvailable
oResults = pService.FindItems(WellKnownFolderName.Contacts, oFilter, oView)
blnMoreAvailable = oResults.MoreAvailable
If Not IsNothing(oResults) AndAlso oResults.Items.Count > 0 Then
For Each oExchangeItem As Item In oResults.Items
Dim oExchangeContactId As ItemId = oExchangeItem.Id
//do something else
Next
If blnMoreAvailable Then oView.Offset = oView.Offset + conMaxChangesReturned
End If
Loop
How to retrieve all contacts from Microsoft Exchange using EWS Managed API?
If your question is how to retrieve the Exchange Contacts using EWS, you're already done with it.
Office365->People is just the right app to manipulate your Exchange contacts. If you check out the People app URL, it's something similar to https://outlook.office365.com/owa/?realm=xxxxxx#exsvurl=1&ll-cc=1033&modurl=2, owa is the synonym for outlook web app.

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

How to programmatically query if an SCCM 2012 Application is Active or Retired

We have an application that integrates with SCCM 2012 and saves custom SCCM applications to SCCM.
The problem I am having is that attempting to save one of our custom applications when the SCCM administrator has set the application to be in the retired state causes our application to fail the saving process.
I'd like to be able to query the SCCM application state in order to determine before we attempt the save operation whether the given application is Active or Retired.
I can find no reference to "retired" status in the SMS_Application Server WMI help or any of the other pages:
http://msdn.microsoft.com/en-us/library/hh949251.aspx
I have noticed that there is a Restore() method which looks like it will change the status of a Retired package back to Active, however that's not quite what I want to do.
Can anyone help me determine how to find an applications current status?
Thanks.
There's a method in the SCCM 2012 PowerShell cmdlets that appears to be retrieving the expired status. Here's the c# code (decompiled from the dll AppUI.PS.AppMan.dll on the SCCM server)
private bool IsApplicationRetired(IResultObject applicaction)
{
IResultObject[] resultObjectArray = null;
int integerValue = applicaction["CI_ID"].IntegerValue;
object[] objArray = new object[] { integerValue };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT * FROM SMS_Application WHERE CI_ID = {0}", objArray));
IResultObject[] resultObjectArray1 = resultObjectArray;
int num = 0;
if (num < (int)resultObjectArray1.Length)
{
IResultObject resultObject = resultObjectArray1[num];
this.isApplicationRetired = resultObject["IsExpired"].BooleanValue;
}
if (this.isApplicationRetired)
{
object[] objArray1 = new object[] { integerValue };
IResultObject instance = base.ConnectionManager.GetInstance(string.Format(CultureInfo.InvariantCulture, "SMS_Application.CI_ID={0}", objArray1));
if (instance != null)
{
string stringValue = instance["ModelName"].StringValue;
instance.Dispose();
object[] objArray2 = new object[] { base.ConnectionManager.EscapeQueryString(stringValue, ConnectionManagerBase.EscapeQuoteType.SingleQuote) };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT CI_ID FROM SMS_Application WHERE ModelName = '{0}' AND IsLatest = 1 AND IsExpired = 0", objArray2));
IResultObject[] resultObjectArray2 = resultObjectArray;
int num1 = 0;
if (num1 < (int)resultObjectArray2.Length)
{
IResultObject resultObject1 = resultObjectArray2[num1];
if (resultObject1["CI_ID"].IntegerValue != integerValue)
{
this.isApplicationRetired = false;
}
}
}
}
return this.isApplicationRetired;
}

Resources