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

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.

Related

certChain.ChainStatus and ChainElementStatus is empty or null in custom cert chain validation .net 6

we need to do a custom cert chain validation, got the code from one of the forums
but in the call back certChain.ChainStatus and ChainElementStatus are empty or null.
its not validating anything where chain status is there, its just skipping.
Anybody has any pointers on this issue, are we missing anything
var root = new X509Certificate2(#"c:\root.cer");
var inter = new X509Certificate2(#"inter.cer");
var validCertificates = new[] { root, inter };
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, certChain, policyErrors) =>
{
return ValidateCertificate(httpRequestMessage, cert, certChain, policyErrors, validCertificates);
};
var httpClient = new HttpClient(handler);
private bool ValidateCertificate(HttpRequestMessage httpRequestMessage, X509Certificate2 cert,
X509Chain certChain, SslPolicyErrors policyErrors, X509Certificate2[] validCertificates)
{
if (certChain.ChainStatus.Any(status => status.Status != X509ChainStatusFlags.UntrustedRoot))
return false;
foreach (var element in certChain.ChainElements)
{
foreach (var status in element.ChainElementStatus) ---skipping this step and not getting inside
{
if (status.Status == X509ChainStatusFlags.UntrustedRoot)
{
certificates
if (validCertificates.Any(cert => cert.RawData.SequenceEqual(element.Certificate.RawData)))
continue;
}
return false;
}
}
return true;
}
Those arrays are empty when the element (or overall chain, depending on which one) have no errors.
You're showing a state that is appropriate to a trusted chain.

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?

How to retrieve the extendedProperties associated with emails in the sent folder from exchange

We have created extendedProperties on emails using
val uId = getUniqueId();
val emailExtendedPropDef = new ExtendedPropertyDefinition(uId,"uniqueId", MapiPropertyType.String)
try {
email.setExtendedProperty(emailExtendedPropDef, uId.toString)
email.sendAndSaveCopy()
} catch {
case e: Exception =>
error(s"Exception in setting extended property for user $from", e)
throw e
}
Now we want to iterate over the emails in sent folder and go over the extendedProperties for the emails that have been set
val view = new ItemView(1000)
var extendedPropertyIndex = 0
var bodyList = new ListBuffer[String]()
val propertySet = new PropertySet(BasePropertySet.FirstClassProperties
try {
val findResults = service.findItems(WellKnownFolderName.SentItems, view)
if (findResults.getTotalCount > 0) {
val iterator = findResults.getItems().iterator()
while(iterator.hasNext) {
val item = iterator.next()
val extendedPropertyCollection = item.getExtendedProperties()
println("count is "+extendedPropertyCollection.getCount())
if (extendedPropertyCollection.getCount() > 0)
{
//do some processing
}
}
}
}
We are able to successfully retrieve the items but not their extended properties not sure why
We have been getting the count as 0 eventhough we know for these items we have set the extendedProperty using the above logic ......
It will be of great help if someone could point us in the right direction on why we are receiving 0 count for the extended properties and also our requirement is to retrieve all the emails with extendedProperties set
Update : tried with these options
val emailIdPropDef = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings,"uniqueId", MapiPropertyType.String)
val propertySet = new PropertySet(BasePropertySet.FirstClassProperties, emailIdPropDef)
view.setPropertySet(propertySet)
But still no luck any pointer in the right direction will be of great help
Exchange will only return the extended properties you request it to return. So you need to add the extended property to the PropertySet your using in the FindItems operation and it will then get returned if it has been set on any objects that FindItem returns.eg this property
val emailExtendedPropDef = new ExtendedPropertyDefinition(uId,"uniqueId", MapiPropertyType.String)
needs to be added to this Propertyset
val propertySet = new PropertySet(BasePropertySet.FirstClassProperties)
and that property set should be used on this ItemView
val view = new ItemView(1000)
val.PropertySet = propertySet

CRM SDK 2013 Activity doesn't exist just after creating it

I have a custom comment activity which I'm updating in code and this used to work but has started failing recently. It creates the activity but when I try to retrieve it or execute SetStateResponse on it, I get "tk_comment With Id = 9a1686d1-7d9d-e611-80e3-00155d001104 Does Not Exist" - which doesn't make sense as I've just created it! The activity record shows up against the account but I can't click on it there or do anything (Record is unavailable - The requested record was not found or you do not have sufficient permissions to view it.).
This is the code I'm using. I'd love you to tell me I've made some simple mistake :)
using (_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig))
{
_serviceProxy.EnableProxyTypes();
try
{
tk_comment comment = new tk_comment();
int maxLength = 190; //subject has a max length of 200 characters
if (subject.Length > maxLength)
{
comment.Subject = subject.Substring(0, maxLength);
comment.Description = subject.Substring(maxLength, subject.Length - maxLength);
}
else
{
comment.Subject = subject;
}
comment.RegardingObjectId = entity.ToEntityReference();
comment.ActualStart = CommentDate;
comment.ActualEnd = CommentDate;
comment.ScheduledStart = CommentDate;
comment.ScheduledEnd = CommentDate;
Guid commentID = _serviceProxy.Create(comment);
try
{
tk_comment aComment = (tk_comment)_serviceProxy.Retrieve(tk_comment.EntityLogicalName, commentID, new ColumnSet(allColumns: true));
}
catch (Exception ex)
{
SingletonLogger.Instance.Error("Always an error here " + ex.Message);
}
Account test = (Account) _serviceProxy.Retrieve(Account.EntityLogicalName, entity.Id, new ColumnSet(allColumns: true));
// tk_comment newComment = (tk_comment)_serviceProxy.Retrieve(tk_comment.EntityLogicalName, commentID, new ColumnSet(allColumns: true));
SetStateRequest request = new SetStateRequest();
request.EntityMoniker = new EntityReference(tk_comment.EntityLogicalName, commentID);
request.State = new OptionSetValue((int) tk_commentState.Completed); //completed
request.Status = new OptionSetValue(2); //completed
SetStateResponse response = (SetStateResponse)_serviceProxy.Execute(request); //always an error here too
}
Appreciate any suggestions
Cheers, Mick
It seems like you don't have proper read permission of this custom activity entity.
OR You need to validate all the permissions of this custom entity.

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);
}
}
}

Resources