private void AppendFreeBusyEntry( DateTime startUtc, DateTime endUtc, string subject, string location, string organizer, BusyStatus busyStatus, StringBuilder result) { DateTime startLocal = OlsonUtil.ConvertFromUTC(startUtc, Request.TimeZone); DateTime endLocal = OlsonUtil.ConvertFromUTC(endUtc, Request.TimeZone); int status = ConversionsUtil.ConvertBusyStatusToGoogleResponse(busyStatus); ConversionsUtil.EscapeNonAlphaNumeric(subject, escapedSubject); ConversionsUtil.EscapeNonAlphaNumeric(location, escapedLocation); ConversionsUtil.EscapeNonAlphaNumeric(organizer, escapedOrganizer); result.AppendFormat("['{0}','{1}','{2}','{3}','{4}',{5}]", escapedSubject, DateUtil.FormatDateTimeForGoogle(startLocal), DateUtil.FormatDateTimeForGoogle(endLocal), escapedLocation, escapedOrganizer, status); }
/// <summary> /// Parses the incoming Exchange request from GCal. The requests are of the form: /// [ version #, ID, [list of emails], startdate/enddate, sincedata, timezone] /// /// </summary> /// <param name="rawInput">The incoming GCal request string</param> private void Parse(string rawInput) { if (rawInput != null) { rawInput = rawInput.Trim(); } /* Test that the request is not null or empty */ if (string.IsNullOrEmpty(rawInput)) { throw new GCalExchangeException(GCalExchangeErrorCode.MalformedRequest, "GCalRequest is null or empty."); } log.InfoFormat("Request received from GCal. [body={0}]", rawInput); /* Test that the request has starting and ending brackets */ if (!rawInput.StartsWith("[") || !rawInput.EndsWith("]")) { throw new GCalExchangeException(GCalExchangeErrorCode.MalformedRequest, String.Format("GCalRequest does start and end in brackets: [rawInput:{0}]", rawInput)); } /* Remove the start and end brackets */ string requestContent = rawInput.Remove(0, 1); requestContent = requestContent.Remove(requestContent.Length - 1, 1); /* Request is cleaned to have no ending brackets */ /* Test that the request has an inner bracket pair which (should) contains the usernames */ if (!(requestContent.Contains("[") && requestContent.IndexOf("]") > requestContent.IndexOf("["))) { throw new GCalExchangeException(GCalExchangeErrorCode.MalformedRequest, string.Format("GCalRequest exchange users section is not properly formatted: [rawInput:{0}]", rawInput)); } /* Get the indexes of the start and end username brackets */ int usersStartIndex = requestContent.IndexOf("["); int usersEndIndex = requestContent.IndexOf("]"); int usersLength = usersEndIndex - usersStartIndex + 1; /* Get the usernames string from the request */ string usersString = requestContent.Substring(usersStartIndex, usersLength); /* Remove it from the rest of the request */ requestContent = requestContent.Remove(usersStartIndex, usersLength); /* Remove the brackets from the start and end of the username string */ usersString = usersString.Remove(0, 1); usersString = usersString.Remove(usersString.Length - 1, 1); /* Split the usernames by comma, store them in the request object */ exchangeUsers = usersString.Split(','); // Apply any domain mappings to the user names for (int i = 0; i < exchangeUsers.Length; i++) { string user = exchangeUsers[i].Trim(); exchangeUsers[i] = ConfigCache.MapToLocalDomain(user); } /* Split up the rest of the request */ string[] requestItems = requestContent.Split(','); /* Test that the proper amount of variables remain in the string */ if (requestItems.Length != expectedRequestItems) { throw new GCalExchangeException(GCalExchangeErrorCode.MalformedRequest, String.Format("GCalRequest does not contain the proper amount of variables; Supplied - {0}, Expected - {1}", requestItems.Length, expectedRequestItems)); } /* Retrieve the version and message ids */ versionNumber = requestItems[0].Trim(); messageId = requestItems[1].Trim(); /* Get the start and end date from the request, the two dates are separated by '/' */ string dateString = requestItems[3].Trim(); string[] dateArray = dateString.Split('/'); if (dateArray.Length != 2) { throw new GCalExchangeException(GCalExchangeErrorCode.MalformedRequest, "GCalRequest does not contain sufficient date information, both a start and end date must be supplied"); } startDate = DateUtil.ParseGoogleDate(dateArray[0].Trim()); endDate = DateUtil.ParseGoogleDate(dateArray[1].Trim()); string requestItemSince = requestItems[4].Trim(); /* Get the since field from the request */ try { since = DateUtil.ParseGoogleDate(requestItemSince); } catch (GCalExchangeException ex) { // We don't really use this param anyway and in some cases // we've seen an invalid date log.Warn(String.Format("Ignoring incorrect since request parameter {0}", requestItemSince), ex); since = new DateTime(); } /* Get the current time zone name */ timeZone = OlsonUtil.GetTimeZone(requestItems[5].Trim()); utcStartDate = OlsonUtil.ConvertToUTC(startDate, timeZone); utcEndDate = OlsonUtil.ConvertToUTC(endDate, timeZone); }
/// <summary> /// Merges a users appointment schedule from with appointments generated from a /// GoogleApps feed /// </summary> /// <param name="user">User to update with Google Apps information</param> /// <param name="googleAppsFeed">Source feed to generate appointment information</param> /// <param name="exchangeGateway">Gateway to sync Appointments with</param> /// <param name="window">DateRange to sync for</param> public void SyncUser( ExchangeUser user, EventFeed googleAppsFeed, ExchangeService exchangeGateway, DateTimeRange window) { exchangeGateway.GetCalendarInfoForUser(user, window); if (!user.HaveAppointmentDetail) { // Cannot sync if there is no appointment detail log.InfoFormat("Skipped Sync of {0} due to missing appointment lookup failure", user.Email); return; } List <Appointment> toUpdate = new List <Appointment>(); List <Appointment> toDelete = new List <Appointment>(); List <Appointment> toCreate = new List <Appointment>(); OlsonTimeZone feedTimeZone = OlsonUtil.GetTimeZone(googleAppsFeed.TimeZone.Value); IntervalTree <Appointment> gcalApptTree = CreateAppointments(user, feedTimeZone, googleAppsFeed); /* Iterate through each Free/Busy time block for the user */ foreach (FreeBusyTimeBlock fbtb in user.BusyTimes.Values) { /* Iterate through each appointment for the Free/Busy time block */ foreach (Appointment appt in fbtb.Appointments) { log.Debug(String.Format("Exchange @ '{0} {1}'", appt.Range, ValidateOwnership(appt))); /* Validate that this is a GCalender appoint */ if (ValidateOwnership(appt)) { /* If the GCalender appointments do not contain an * appointment for this period, add it for deletion */ if (gcalApptTree.FindExact(appt.Range) == null) { toDelete.Add(appt); } } } } /* Iterate through each Google Apps appointment */ AppointmentCollection appointments = user.BusyTimes.Appointments; List <Appointment> gcalApptList = gcalApptTree.GetNodeList(); foreach (Appointment newAppt in gcalApptList) { // If the meeting was cancelled log.DebugFormat("Looking @ {0} {1}", newAppt.Range, newAppt.Range.Start.Kind); if (newAppt.MeetingStatus == MeetingStatus.Cancelled) { // Check if there is an existing appointment that matches List <Appointment> matches = appointments.Get(newAppt.Range); foreach (Appointment a in matches) { if (ValidateOwnership(a)) { toDelete.Add(a); } } // Work is done for this appointment, continue to next entry continue; } bool updatedAppointment = false; List <Appointment> apptList = appointments.Get(newAppt.Range); log.DebugFormat("Looking up preexisting event: {0} {1}", newAppt.Range, newAppt.Range.Start.Kind); log.DebugFormat("Found {0} matching items", apptList.Count); // Check that there is a free busy block that correlates with this appointment foreach (Appointment existingAppt in apptList) { if (ValidateOwnership(existingAppt) && !updatedAppointment) { UpdateAppointmentInfo(existingAppt, newAppt); toUpdate.Add(existingAppt); updatedAppointment = true; } } if (!updatedAppointment) { toCreate.Add(newAppt); log.DebugFormat("ADDING '{0}' - Not an update", newAppt.Range); } } if (log.IsInfoEnabled) { log.InfoFormat( "AppointmentWriter for '{0}'. [{1} deleted, {2} updated, {3} new]", user.Email, toDelete.Count, toUpdate.Count, toCreate.Count); } exchangeGateway.Appointments.DeleteAppointments(user, toDelete); // TODO: Updates are not currently published // exchangeGateway.Appointments.UpdateAppointments( user, updateAppointments ); exchangeGateway.Appointments.WriteAppointments(user, toCreate); }