/// <summary> /// Generates a response from a GCalRequest /// </summary> /// <returns></returns> public string GenerateResponse() { /* Create a string builder to hold the text for the response */ StringBuilder result = new StringBuilder(4096); /* Create an exchange provider */ ExchangeService gateway = new ExchangeService( ConfigCache.ExchangeServerUrl, ConfigCache.ExchangeUserLogin, ConfigCache.ExchangeUserPassword); /* Return the exchangers from the GCal Request that was passed in */ DateTimeRange range = new DateTimeRange(request.UTCStartDate, request.UTCEndDate); ExchangeUserDict exchangeUsers = gateway.SearchByEmail(range, request.ExchangeUsers); /* Create the header of the request */ result.AppendFormat("['{0}','{1}',", request.VersionNumber, request.MessageId); result.AppendFormat("['_ME_AddData','{0}/{1}','{2}'", DateUtil.FormatDateForGoogle(request.StartDate), DateUtil.FormatDateForGoogle(request.EndDate), DateUtil.FormatDateTimeForGoogle(request.Since)); /* Flag for inserting commas */ bool firstUser = true; result.Append(",["); foreach (ExchangeUser user in exchangeUsers.Values) { /* Don't add a comma if this is the first user */ if (!firstUser) { result.Append(","); } /* Add the user's credentials */ string email = ConfigCache.MapToExternalDomain(user.Email); result.AppendFormat("'{0}','{1}','{2}',[", user.DisplayName, email, (int)user.AccessLevel); GenerateResponseForTimeBlocks(user, result); result.Append("]"); firstUser = false; } result.Append("]"); result.Append("]"); result.Append("]"); log.Info("GCal Free/Busy response successfully generated."); log.DebugFormat("Response = {0}", result); return(result.ToString()); }
public void TestParseWithDomainMap() { string id = "test-id"; List <string> srcEmails = new List <string>(); srcEmails.Add("*****@*****.**"); srcEmails.Add("*****@*****.**"); srcEmails.Add("*****@*****.**"); srcEmails.Add("*****@*****.**"); List <string> dstEmails = new List <string>(); dstEmails.Add("*****@*****.**"); dstEmails.Add("*****@*****.**"); dstEmails.Add("*****@*****.**"); dstEmails.Add("*****@*****.**"); DateTime start = DateUtil.ParseDateToUtc("2008-01-01T00:00:00.000Z"); DateTime end = DateUtil.ParseDateToUtc("2008-01-31T00:00:00.000Z"); DateTime since = DateUtil.ParseDateToUtc("2008-01-01T00:00:00.000Z"); string tz = "America/Los_Angeles"; string query = string.Format("[ 1, {0}, [{1}], {2}/{3}, {4}, {5}]", id, createEmailList(srcEmails), createDate(start), createDate(end), createDateTime(since), tz); ConfigCache.AddDomainMap("example.org", "woot.org"); GCalFreeBusyRequest request = new GCalFreeBusyRequest(query); Assert.AreEqual(start, request.StartDate); Assert.AreEqual(end, request.EndDate); Assert.AreEqual(since, request.Since); Assert.AreEqual(id, request.MessageId); Assert.AreEqual("1", request.VersionNumber); Assert.AreEqual(dstEmails.Count, request.ExchangeUsers.Length); for (int i = 0; i < request.ExchangeUsers.Length; i++) { Assert.AreEqual(dstEmails[i], request.ExchangeUsers[i]); } }
/// <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> /// Query Google Calendar for a user and return the event feed /// </summary> /// <param name="email">Email address of the user to query</param> /// <param name="visibility">Feed Visibility (Public/Private) to query for</param> /// <param name="projection">Feed projection - type of feed to get</param> /// <param name="modifiedSince">Last modified time from last check</param> /// <param name="window">DateTime range to query between</param> /// <returns>An event feed for the user</returns> public EventFeed QueryGCal( string email, GCalVisibility visibility, GCalProjection projection, DateTime modifiedSince, DateTimeRange window) { // Perform mapping on the username if necessary string user = ConfigCache.MapToExternalDomain(email); if (log.IsDebugEnabled) { log.InfoFormat( "FeedQuery with parameters: {0}, {1}, {2}, {3} [{4}]", user, visibility, projection, modifiedSince, window); } StringBuilder sb = new StringBuilder(ConfigCache.GCalAddress); if (!ConfigCache.GCalAddress.EndsWith("/")) { sb.Append("/"); } sb.AppendFormat("feeds/{0}/", user); switch (visibility) { case GCalVisibility.Public: sb.Append("public/"); break; case GCalVisibility.Private: default: sb.Append("private/"); break; } switch (projection) { case GCalProjection.Full: sb.Append("full"); break; case GCalProjection.FullNoAttendees: sb.Append("full-noattendees"); break; case GCalProjection.Composite: sb.Append("composite"); break; case GCalProjection.AttendeesOnly: sb.Append("attendees-only"); break; case GCalProjection.FreeBusy: sb.Append("free-busy"); break; case GCalProjection.Basic: default: sb.Append("basic"); break; } EventQuery query = new EventQuery(sb.ToString()); if (projection != GCalProjection.FreeBusy) { query.SingleEvents = true; } GDataRequestFactory f = (GDataRequestFactory)service.RequestFactory; f.UseGZip = ConfigCache.EnableHttpCompression; if (window.Start != DateTime.MinValue) { query.StartTime = window.Start; } if (window.End != DateTime.MaxValue) { query.EndTime = window.End; } query.NumberToRetrieve = int.MaxValue; // Make sure we get everything try { return(QueryGCal(query, user, modifiedSince)); } catch (System.IO.IOException e) { // Known problem with .NET 2.0 - Sometimes keep-alive connection is // closed by a proxy and we need to re-attemp the connection // // http://code.google.com/p/google-gdata/wiki/KeepAliveAndUnderlyingConnectionIsClosed if (e.InnerException.GetType().ToString().Equals("System.Net.Sockets.SocketException")) { log.Info(String.Format("Attempt Retry Query after keep-alive termination")); // One shot retry i case the keep-alive was closed return(QueryGCal(query, user, modifiedSince)); } else { throw; } } }