EWS Appointment server change timestamp - c#

I'm working on buffer solution for MS Exchange server.
The buffer should store events and keep them in sync with Exchange server.
I code in C# and use Managed EWS.
The buffer queries Exchange for appointments changed in sequential intervals:
(t[1], t[2]), (t[2], t[3]), (t[3], t[4])
...
I use Appointment.LastModifiedTime field to filter out appointments changed in interval (t[x], t[x+1]). However I discovered, that this field takes it value from time of modification on client. What I need is some timestamp of modification on server.
Imagine the case: user X goes offline and adds appointment on t[Y] in his Outlook client. On t[Z] = t[Y] + 24h user X goes online. His Outlook client will sync with Exchange server and this new appoinment appears on it with LastModifiedDate = t[Y]. Is there some field to get t[Z] ?
For now I see full sync as a 100% working alternative to this, but it is a 'hammer solution' with obvious drawbacks. I will have to use it in case nobody can give an alternative :)

Related

How to stop meeting being stripped on forward (EWS)

I am using C# with the Exchange Web Service (EWS).
I have meetings that I need to forward and whenever I do they get stripped, but only from some accounts and not others. The link for the meeting is still there but it is not being recognised by the online Outlook as a meeting item, nor by Teams which is connected to the account.
This even happens if I manually forward, but again only if I forward emails that are from some accounts - some other accounts are fine!
I'm using this on incoming emails:
var fwdEmailArr = new EmailAddress[1];
fwdEmailArr [0] = fwdEmail;
MeetingRequest appointment = MeetingRequest.Bind(service, email.Id);
appointment.Forward("", fwdEmailArr);
This is the same issue if I use the email.forward as well, etc.
However, if I create a new appointment and send it, it doesn't get stripped - this is with the same addresses.
Appointment appt = new Appointment(service);
appt.Subject = email.Subject;
appt.Body = appointment.Body;
appt.Start = appointment.Start;
appt.End = appointment.End;
appt.Location = appointment.Location;
appt.RequiredAttendees.Add(fwdEmail);
foreach (var reqAtt in appt.RequiredAttendees)
{
appt.RequiredAttendees.Add(reqAtt);
}
foreach (var reqAtt in appt.OptionalAttendees)
{
appt.OptionalAttendees.Add(reqAtt);
}
appt.RequiredAttendees.Add(appointment.From.Address);
appt.Save(SendInvitationsMode.SendToAllAndSaveCopy);
So, I could do this but it means that they are no longer the same meeting and declining the original wont decline this. Unless there's a way I can connect the meetings or something?
Any ideas how I can stop the meeting being stripped?
Or alternatively just add another recipient to the current meeting, that will show on their calendar?
If anyone comes here with a similar issue, it turns out that, first of all you need to make sure you define the correct server version on the service declaration:
service = new ExchangeService(ExchangeVersion.Exchange2016){}
In addition, for some reason some images when attached to the forwarded email for some reason confuse EWS and make it think there's no meeting. I got around this by scanning the MIME content and just extracting the calendar block and deleting all other attachments.
This has been working flawlessly for about 5 months.

Simplified LDAP/AD Server on C#

I've searched without much success to the simplest (and yet working) example of an LDAP/AD Server for C#. Many libraries exist to connect to LDAP servers, but not the LDAP Server by itself (on C#).
I found however some information about it and even a post requesting a simple LDAP server that was answered "LDAP isn't simple"; and yet i read a lot of the RFC4511 and this sample code at GitHub Flexinet LDAP Server, but unfortunatly i don't have yet the knowledge to complete it's code.
My goal is not to make a fully functional LDAP server, but one that can at least do:
Serve as a login pool for softwares that allow it's users to be
registered and log on a AD/LDAP server (just check for login and
password for authentication).
Allow softwares like Outlook and Thunderbird to get a list of users (without passwords) with first and last name, e-mail address, phone number and department for contact list model.
No delete, add (or create), move, and other
functions are required since the main software that i aim to
integrate it with will do all the user and group management.
UPDATE
I'm trying to implement the Flexinet sample and adjust to that functionalities; as form of a question what should i do to change this function to prevent it from causing an exception (on the "var filter = searchRequest.ChildAttributes[6];" line it always breaks) when i call from a LDAP client software:
private void HandleSearchRequest(NetworkStream stream, LdapPacket requestPacket)
{
var searchRequest = requestPacket.ChildAttributes.SingleOrDefault(o => o.LdapOperation == LdapOperation.SearchRequest);
var filter = searchRequest.ChildAttributes[6];
if ((LdapFilterChoice)filter.ContextType == LdapFilterChoice.equalityMatch && filter.ChildAttributes[0].GetValue<String>() == "sAMAccountName" && filter.ChildAttributes[1].GetValue<String>() == "testuser") // equalityMatch
{
var responseEntryPacket = new LdapPacket(requestPacket.MessageId);
var searchResultEntry = new LdapAttribute(LdapOperation.SearchResultEntry);
searchResultEntry.ChildAttributes.Add(new LdapAttribute(UniversalDataType.OctetString, "cn=testuser,cn=Users,dc=dev,dc=company,dc=com"));
searchResultEntry.ChildAttributes.Add(new LdapAttribute(UniversalDataType.Sequence));
responseEntryPacket.ChildAttributes.Add(searchResultEntry);
var responsEntryBytes = responseEntryPacket.GetBytes();
stream.Write(responsEntryBytes, 0, responsEntryBytes.Length);
}
var responseDonePacket = new LdapPacket(requestPacket.MessageId);
responseDonePacket.ChildAttributes.Add(new LdapResultAttribute(LdapOperation.SearchResultDone, LdapResult.success));
var responseDoneBytes = responseDonePacket.GetBytes();
stream.Write(responseDoneBytes, 0, responseDoneBytes.Length);
}
The code is on the github link.
Finally i made a fork of the Flexinet LDAP Server on #Sammuel-Miranda/LdapServerLib and with the author's support and some changes and adaptations i completed this implementation. It responds to the bind and search calls and works perfectly for Outlook and Thunderbird to use as a shared address book.
I did not implemente however any ADD/MODIFY/DELETE request (but would not be hard to do) since i don't need then.
I found on the RFC4511 the explanation on how the search works ... and i'm "kind" of understanding it, not very well - and i see that the method implemented on the GitHub from Flexinet LDAP Server only answer to bind and search requests of one single user (since it's only a example implementation).
The client is requesting diferent calls to verify capabilities, structure and other info before making the search request itself. So i'll implement it all, one by one.
Still, if any other lib (in C#) exists, and anyone know about, would be better than writing a hole new server. If my implementation works, i'll fork it on github and share.

EWS Exchange Impersonate 2 or more users using one ExchangeService connection

In order to avoid the Exchange Server limit RCAMaxConcurrency (0-100), I want to code a listener service (streamlistener) that can handle up to 5000 users using just one connection. I already have 200 test accounts and another account having impersonate rights over those 200 test accounts.
If possible it would be nice to avoid switching around all accounts.
We already have the code and it works fine doing impersonation just to 1 user.
public void SuscribeToCalendar()
{
// Set the email address of the account to get the appointment.
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.PrincipalName, "xxxxxxxxx");
// Subscribe to streaming notifications in the Inbox.
StreamingSubscription streamingSubscription = service.SubscribeToStreamingNotifications(
new FolderId[] { WellKnownFolderName.Calendar }, EventType.Created, EventType.Modified, EventType.Moved);
// Create a streaming connection to the service object, over which events are returned to the client.
// Keep the streaming connection open for 30 minutes.
StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(streamingSubscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnDisconnect += OnDisconnect;
connection.Open();
}
RCAMaxConcurrency doesn't affect EWS it affect Outlook connections which use RPC to connect. What will affect EWS is EWSMaxConcurrency which has a much a lower value to 10 by default. (you would also get affected by EWSMaxSubscriptions which is 20).
The impersonation setting affect the header of the EWS request so you can't impersonate any more the one user per call so when creating a subscription you need one call per user you subscribing. You can group the Subscripting together into one connection with impersonation using the following https://msdn.microsoft.com/en-us/library/office/dn458789(v=exchg.150).aspx .
The limitations with group is 200 users per group, given the amount of churn you have with subscriptions you don't really want any more the this. When your using impersonation the number of connections isn't an issue as long as you don't anchor ever group with the same mailbox.
Cheers
Glen

get mails in a conversation for exchange mails

I want to retrieve all mails in a conversation when a user selects an email.
I know that I can use this - https://msdn.microsoft.com/en-us/library/office/ff869870(v=office.15).aspx, but for some microsoft exchange accounts, getrootitems returning zero.
So, is there any other way that works for microsoft exchange ?
More details:
My outlook showing "Online with microsoft exchange" at bottom right.
Below process is slow as I have 1000's of mails in inbox, so this wont help me.
IEnumerable mail =
folder.Items.OfType().Where(m => m.Subject == "Test").Select(m => m);
The GetRootItems method of the Conversation class has the following description in MSDN:
If all items are deleted from the conversation after the Conversation object has been obtained, GetRootItems returns a SimpleItems collection with zero objects. In this case, the Count property of the SimpleItems collection returns 0.
Try to use the cached exchange mode instead.

how to use GMAIL API query filter for datetime

I am using GMAIL API over REST interface to read mails from gmail server, my problem is when I am using date filter by giving a date as 'after:2014/8/20 before:2014/8/22' then the mails starting from 2014/8/20 12.30 PM onwards are downloaded (ideally it should consider mails from 12.00 AM). Mails from night 12.00 AM till noon 12.30 PM are skipped. I think server is using PST time zone.
Can I specify time in the filter? or is there a way to specify time zone so that I get all the mails.
code used:
UsersResource.MessagesResource.ListRequest request = null;
ListMessagesResponse response = null;
request = gmailServiceObj.Users.Messages.List(userEmail);
String query = "after:" + FromDate.Date.ToString("yyyy/M/dd") + " before:" + ToDate.Date.ToString("yyyy/M/dd") + " label:" + LabelID;
request.Q = query;
Thanks,
Haseena
The behavior of the API in this regard should be the same as the web UI, can you verify if that's not the case? The search query params are listed here:
https://support.google.com/mail/answer/7190?hl=en
It seems odd that it wouldn't deliver emails between 12:00AM and 12:30AM, what timezone is your client in? What's the timezone preference set to in the Gmail web interface for the user? You could try changing that preference and see if it helps? If not, one workaround I can think of is to have the filter from the day before and do the filtering client-side, as ugly as that is... :-/
It does appear that the timezone being used when processing these queries is always PST. There is currently no way to specify the timezone in the request, or have it use the timezone of the account. I'll follow up with the engineering team to come up with a resolution.

Categories

Resources