/// <summary> /// Queries AD for all Groupwise connector contacts and returns /// a dictionary of GWiseContacts. The key is the UID of the contact. /// If no contacts are found an empty dictionary will be returned, /// no null. /// The root path, user name and password are optional. /// If they are given they will be used, otherwise RootDSE /// and the current user will be used instead. /// In addition to all contacts function returns the /// URL for the folder that contains the free busy message for /// the accounts. /// </summary> /// <param name="searchRootPath">The LDAP root to search, /// null will search the default domain</param> /// <param name="userName">User to use to make the query, /// null will use the default credentials</param> /// <param name="password">Password for the user</param> /// <param name="filter">The LDAP filter for the query</param> /// <param name="freeBusyUrl"> /// Will be set to the free busy folder URL</param> /// <returns>Dictionary of all contacts keyed by the UID</returns> private static Dictionary <string, GWiseContact> GetGWiseContactsFromAD( string searchRootPath, string userName, string password, out string freeBusyUrl) { freeBusyUrl = null; Dictionary <string, GWiseContact> gwise_contacts = new Dictionary <string, GWiseContact>(); SearchResultCollection adContacts = FindGWiseContacts( searchRootPath, userName, password, GWISE_CONTACTS_QUERY, PROPERTIES_TO_LOAD); foreach (SearchResult adContact in adContacts) { ResultPropertyCollection contactProps = adContact.Properties; GWiseContact gwiseContact = ParseGWiseContactsFromADProperties( contactProps, ref freeBusyUrl); if (gwiseContact != null) { gwise_contacts.Add( gwiseContact.GwiseUid, gwiseContact); } } return(gwise_contacts); }
/// <summary> /// The function gets a stream, which is the result of WebDAV query /// for free busy emails. It parses the contacts UIDs from the XML /// response and marks the contacts, which have free buys emails. /// If the subject is mallformed the function will return null. /// I am not perfectly happy about mixing the response and /// the dictionary in a single function. It will be cleaner to parse /// the response and have this function work on a list. /// the problem with that is the number of additional allocations that /// are going to be made, just to be thrown away. So the current design /// is a compromise for better performance. /// </summary> /// <param name="gwiseContacts">Dictionary of contacts</param> /// <param name="stream">Http response from Exchange</param> private static void MarkWorkingContacts( Dictionary <string, GWiseContact> gwiseContacts, Stream stream) { XmlReaderSettings settings = new XmlReaderSettings(); settings.XmlResolver = null; settings.IgnoreComments = true; settings.IgnoreWhitespace = true; settings.ValidationType = ValidationType.None; settings.ValidationFlags = XmlSchemaValidationFlags.None; XmlReader reader = XmlReader.Create(stream, settings); reader.MoveToContent(); while (reader.Read()) { if ((reader.NodeType == XmlNodeType.Element) && (reader.Name.IndexOf(COLON_SUBJECT) != -1)) { string element = reader.ReadElementString(); string uid = GetGWiseUidFromFreeBusySubject(element); if (uid != null) { GWiseContact gwiseContact = null; if (gwiseContacts.TryGetValue(uid, out gwiseContact)) { gwiseContact.Marked = true; } } reader.ReadEndElement(); } } }
/// <summary> /// The function creates a free busy email for the given GWise contact /// under the public folder specified in free_busy_url. /// The credentials are optional. /// If they are given they will be used, otherwise /// the current user will be used to make the WebDAV call. /// </summary> /// <param name="userName">User to authenticate as or /// null will use the default credentials</param> /// <param name="password">Password for the user</param> /// <param name="freeBusyUrl">The Url of the free busy folder, /// where the free busy email should be created</param> /// <param name="gwiseContact">The contact for /// which to create the free busy email</param> private static void CreateGWiseFreeBusyEmail( ICredentials credentials, string freeBusyUrl, GWiseContact gwiseContact) { string gwiseUid = gwiseContact.GwiseUid; string gwiseAddress = gwiseContact.GwiseAddress; string commonGroup = gwiseContact.CommonGroup; string freeBusyMessageUrl = string.Format(FREE_BUSY_EML_TEMPLATE, freeBusyUrl, commonGroup, gwiseUid); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(freeBusyMessageUrl); if (credentials != null) { request.Credentials = credentials; request.ConnectionGroupName = "CustomCredentials"; } else { request.UseDefaultCredentials = true; request.ConnectionGroupName = "DefaultNetworkCredentials"; } request.Method = PROPPATCH; // This is necesssary to make Windows Auth use keep alive. // Due to the large number of connections we may make to Exchange, // if we don't do this, the process may exhaust the supply of // available ports. // To keep this "safe", requests are isolated by connection pool. // See UnsafeAuthenticatedConnectionSharing on MSDN. request.UnsafeAuthenticatedConnectionSharing = true; request.PreAuthenticate = true; request.AllowAutoRedirect = false; request.KeepAlive = true; request.UserAgent = USER_AGENT; request.Accept = SLASH_START_SLASH; request.ContentType = TEXT_SLASH_XML; request.Headers.Add("Translate", "F"); request.Headers.Add("Brief", "t"); string propPatchRequest = string.Format(PROPPATCH_REQUEST, commonGroup, gwiseUid, gwiseAddress); byte[] encodedBody = Encoding.UTF8.GetBytes(propPatchRequest); request.ContentLength = encodedBody.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(encodedBody, 0, encodedBody.Length); requestStream.Close(); WebResponse response = (HttpWebResponse)request.GetResponse(); Stream responseStream = response.GetResponseStream(); responseStream.Close(); response.Close(); }
/// <summary> /// Parses the properties for a contact that came from AD /// and return GWiseContact object created from those properties. /// If some of the properties are misisng or mallformed /// the function will return null. /// In addition to parsing the contact the function returns the /// URL for the folder that contains the free busy message for /// the account, if it wasn't computed yet. /// </summary> /// <param name="contactProps">The properties of the contact</param> /// <param name="freeBusyUrl">If not already computed, /// it will be set to the free busy folder URL</param> /// <returns>A contact object or null</returns> private static GWiseContact ParseGWiseContactsFromADProperties( ResultPropertyCollection contactProps, ref string freeBusyUrl) { string gwiseUid = null; string gwiseAddress = null; string commonGroup = null; string freeBusyUrlTemp = null; GWiseContact gwiseContact = null; foreach (string propName in contactProps.PropertyNames) { foreach (Object propObject in contactProps[propName]) { string propValue = propObject.ToString(); if ((freeBusyUrl == null) && (freeBusyUrlTemp == null)) { freeBusyUrlTemp = GenerateParentFreeBusyFolderUrl( propName, propValue); } if (gwiseUid == null) { gwiseUid = GetGWiseUidFromLegacyExchangeDN( propName, propValue); } if (commonGroup == null) { commonGroup = GetCommonGroupFromLegacyExchangeDN( propName, propValue); } if (gwiseAddress == null) { gwiseAddress = GetGWiseAddressFromProxyAddresses( propName, propValue); } } } if ((gwiseAddress != null) && (gwiseUid != null) && (commonGroup != null)) { gwiseContact = new GWiseContact(gwiseUid, gwiseAddress, commonGroup); } if ((freeBusyUrl == null) && (gwiseContact != null) && (freeBusyUrlTemp != null)) { // Return the free busy URL if not set already, // but do that only for well formed accounts. freeBusyUrl = freeBusyUrlTemp; } return gwiseContact; }
/// <summary> /// Parses the properties for a contact that came from AD /// and return GWiseContact object created from those properties. /// If some of the properties are misisng or mallformed /// the function will return null. /// In addition to parsing the contact the function returns the /// URL for the folder that contains the free busy message for /// the account, if it wasn't computed yet. /// </summary> /// <param name="contactProps">The properties of the contact</param> /// <param name="freeBusyUrl">If not already computed, /// it will be set to the free busy folder URL</param> /// <returns>A contact object or null</returns> private static GWiseContact ParseGWiseContactsFromADProperties( ResultPropertyCollection contactProps, ref string freeBusyUrl) { string gwiseUid = null; string gwiseAddress = null; string commonGroup = null; string freeBusyUrlTemp = null; GWiseContact gwiseContact = null; foreach (string propName in contactProps.PropertyNames) { foreach (Object propObject in contactProps[propName]) { string propValue = propObject.ToString(); if ((freeBusyUrl == null) && (freeBusyUrlTemp == null)) { freeBusyUrlTemp = GenerateParentFreeBusyFolderUrl( propName, propValue); } if (gwiseUid == null) { gwiseUid = GetGWiseUidFromLegacyExchangeDN( propName, propValue); } if (commonGroup == null) { commonGroup = GetCommonGroupFromLegacyExchangeDN( propName, propValue); } if (gwiseAddress == null) { gwiseAddress = GetGWiseAddressFromProxyAddresses( propName, propValue); } } } if ((gwiseAddress != null) && (gwiseUid != null) && (commonGroup != null)) { gwiseContact = new GWiseContact(gwiseUid, gwiseAddress, commonGroup); } if ((freeBusyUrl == null) && (gwiseContact != null) && (freeBusyUrlTemp != null)) { // Return the free busy URL if not set already, // but do that only for well formed accounts. freeBusyUrl = freeBusyUrlTemp; } return(gwiseContact); }