// CheckNonSecuredEndpointForRedirect // This function does an unauthenticated GET request to a non-https // URL to see if the server will respond with a 302 redirect. If so, it will // attempt to use the value of the Location header to perform Autodiscover. // // Parameters: // useSoapEndpoints: Indicates whether SOAP endpoints should be tried. // // Returns: // A Dictionary object that contains the settings returned by the // Autodiscover process. If null, either the redirect failed or the URL failed. // private Dictionary <string, string> CheckNonSecuredEndpointForRedirect(bool useSoapEndpoints) { // Get the non-https URL, or "last ditch" URL. string nonHttpsEndpoint = UrlList.GetLastDitchUrl(EmailAddress); Tracing.WriteLine("Sending non-authenticated GET to " + nonHttpsEndpoint); HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(nonHttpsEndpoint); getRequest.Method = "GET"; getRequest.AllowAutoRedirect = false; getRequest.PreAuthenticate = false; try { HttpWebResponse getResponse = (HttpWebResponse)getRequest.GetResponse(); if (getResponse != null) { if (getResponse.StatusCode == HttpStatusCode.Redirect || getResponse.StatusCode == HttpStatusCode.Moved || getResponse.StatusCode == HttpStatusCode.RedirectKeepVerb || getResponse.StatusCode == HttpStatusCode.RedirectMethod) { Tracing.WriteLine("Received a redirect status: " + getResponse.StatusCode.ToString()); string redirectUrl = getResponse.Headers["Location"].ToString(); Tracing.WriteLine("Location header: " + (string.IsNullOrEmpty(redirectUrl) ? "MISSING" : redirectUrl)); if (IsValidRedirectUrl(redirectUrl)) { // You got a valid redirect; try it. UrlRedirects++; return(TryAutodiscoverUrl(AutodiscoverUrlList.NormalizeAutodiscoverUrl(redirectUrl), useSoapEndpoints)); } else { Tracing.WriteLine("Redirect returned missing or invalid URL, unable to proceed."); } } else { Tracing.WriteLine("Received a non-redirect status: " + getResponse.StatusCode.ToString()); } } } catch (WebException e) { Tracing.WriteLine("Unable to connect."); Tracing.WriteLine(e.ToString()); } return(null); }
// TryPoxAutodiscoverUrl // This function trys a URL as a POX endpoint. // // Parameters: // url: The URL to try. Note that this value is a POX URL, meaning // it has a .xml file extension. ("https://contoso.com/autodiscover/autodiscover.xml") // useSoapEndpoints: Indicates whether SOAP endpoints should be tried. // // Returns: // A Dictionary object that contains the settings returned by the // Autodiscover process. If null, the URL failed. // private Dictionary <string, string> TryPoxAutodiscoverUrl(string url, bool useSoapEndpoints) { Tracing.WriteLine("Trying " + url); Dictionary <string, string> settingsDictionary = null; // Generate the POX request. XElement autodiscover = new XElement(PoxXmlStrings.Autodiscover, new XElement(PoxXmlStrings.Request, new XElement(PoxXmlStrings.EMailAddress, EmailAddress), new XElement(PoxXmlStrings.AcceptableResponseSchema, "http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a") ) ); Tracing.WriteLine("POX Request:"); PrintElement(autodiscover); try { HttpWebRequest poxRequest = (HttpWebRequest)WebRequest.Create(url); poxRequest.AllowAutoRedirect = false; poxRequest.Credentials = this.Credentials; poxRequest.Method = "POST"; poxRequest.ContentType = "text/xml"; Stream requestStream = poxRequest.GetRequestStream(); autodiscover.Save(requestStream); requestStream.Close(); HttpWebResponse poxResponse = (HttpWebResponse)poxRequest.GetResponse(); if (poxResponse.StatusCode == HttpStatusCode.OK) { // Successful response. Stream responseStream = poxResponse.GetResponseStream(); XElement responseAutodiscover = XElement.Load(responseStream); if (responseAutodiscover != null) { Tracing.WriteLine("Response:"); PrintElement(responseAutodiscover); // If no errors, it is safe to proceed. string error = CheckPoxResponseForError(responseAutodiscover); if (error == "NoError") { // Check Action value. XElement action = FindFirstDescendant(responseAutodiscover, PoxXmlStrings.Action); if (action != null) { if (action.Value == "settings") { settingsDictionary = new Dictionary <string, string>(); IEnumerable <XElement> protocols = from protocol in responseAutodiscover.Descendants (PoxXmlStrings.Protocol) select protocol; foreach (XElement protocol in protocols) { XElement type = protocol.Element(PoxXmlStrings.Type); if (type != null && type.Value == "EXCH") { // You found what you are looking for. AddPoxSettingToDictionary(settingsDictionary, protocol, PoxXmlStrings.EwsUrl); break; } } } else if (action.Value == "redirectAddr") { // The server has given you a better email address to use. Tracing.WriteLine("Server response contains a RedirectAddr element."); XElement redirectAddr = FindFirstDescendant(responseAutodiscover, PoxXmlStrings.RedirectAddr); if (redirectAddr != null && IsValidRedirectAddress(redirectAddr.Value, EmailAddress)) { Tracing.WriteLine("Restarting Autodiscover with email address: " + redirectAddr.Value); EmailAddress = redirectAddr.Value; UrlList.Clear(); UrlList.GenerateList(this.EmailAddress); visitedUrls.Clear(); AddressRedirects++; return(TryAutodiscoverUrls(useSoapEndpoints)); } Tracing.WriteLine("Invalid or missing redirectAddr element, continuing..."); } else if (action.Value == "redirectUrl") { // The server has given you a better URL // to use. Validate that it is https and // try it. Tracing.Write("Server response contains a RedirectUrl element for url: "); XElement redirectUrl = FindFirstDescendant(responseAutodiscover, PoxXmlStrings.RedirectUrl); if (redirectUrl != null && IsValidRedirectUrl(redirectUrl.Value.ToLower())) { Tracing.WriteLine(redirectUrl.Value); UrlRedirects++; return(TryAutodiscoverUrl( AutodiscoverUrlList.NormalizeAutodiscoverUrl(redirectUrl.Value), useSoapEndpoints)); } else { Tracing.WriteLine("Missing or invalid RedirectUrl element, continuing..."); } } } } } } else if (poxResponse.StatusCode == HttpStatusCode.Redirect || poxResponse.StatusCode == HttpStatusCode.Moved || poxResponse.StatusCode == HttpStatusCode.RedirectKeepVerb || poxResponse.StatusCode == HttpStatusCode.RedirectMethod) { // Redirect HTTP status scenario. Tracing.WriteLine("Received a redirect status: " + poxResponse.StatusCode.ToString()); string redirectUrl = poxResponse.Headers["Location"].ToString(); Tracing.WriteLine("Location header: " + (string.IsNullOrEmpty(redirectUrl) ? "MISSING" : redirectUrl)); if (IsValidRedirectUrl(redirectUrl.ToLower())) { UrlRedirects++; return(TryAutodiscoverUrl(AutodiscoverUrlList.NormalizeAutodiscoverUrl(redirectUrl), useSoapEndpoints)); } Tracing.WriteLine("Invalid or missing redirect URL returned. Unable to proceed."); return(null); } } catch (WebException e) { Tracing.WriteLine("Error connection:"); Tracing.WriteLine(e.ToString()); } return(settingsDictionary); }
public AutodiscoverRequest(string email, NetworkCredential creds) { EmailAddress = email; Credentials = creds; UrlList = new AutodiscoverUrlList(); }
// TrySoapAutodiscoverUrl // This function trys a URL as a SOAP endpoint. // NOTE: The SOAP Autodiscover service is only available in versions of Exchange starting // with Exchange 2010, including Exchange Online. // // Parameters: // url: The URL to try. Note that this value is a SOAP URL, meaning // it has a .svc file extension. ("https://contoso.com/autodiscover/autodiscover.svc") // useSoapEndpoints: Indicates whether SOAP endpoints should be tried. // // Returns: // A Dictionary object that contains the settings returned by the // Autodiscover process. If null, the URL failed. // private Dictionary <string, string> TrySoapAutodiscoverUrl(string url, bool useSoapEndpoints) { Tracing.WriteLine("Trying " + url); Dictionary <string, string> settingsDictionary = null; // Generate the SOAP request. XElement envelope = new XElement(SoapXmlStrings.Envelope, new XElement(SoapXmlStrings.Header, new XElement(SoapXmlStrings.RequestedServerVersion, SoapXmlStrings.MinServerVersion), new XElement(SoapXmlStrings.Action, "http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetUserSettings"), new XElement(SoapXmlStrings.To, url) ), new XElement(SoapXmlStrings.Body, new XElement(SoapXmlStrings.GetUserSettingsRequestMessage, new XElement(SoapXmlStrings.Request, new XElement(SoapXmlStrings.Users, new XElement(SoapXmlStrings.User, new XElement(SoapXmlStrings.Mailbox, EmailAddress) ) ), new XElement(SoapXmlStrings.RequestedSettings, new XElement(SoapXmlStrings.Setting, "InternalEwsUrl"), new XElement(SoapXmlStrings.Setting, "ExternalEwsUrl"), new XElement(SoapXmlStrings.Setting, "ExternalEwsVersion"), new XElement(SoapXmlStrings.Setting, "EwsSupportedSchemas") ) ) ) ) ); Tracing.WriteLine("SOAP Request:"); PrintElement(envelope); try { HttpWebRequest soapRequest = (HttpWebRequest)WebRequest.Create(url); soapRequest.AllowAutoRedirect = false; soapRequest.Credentials = this.Credentials; soapRequest.Method = "POST"; soapRequest.ContentType = "text/xml"; Stream requestStream = soapRequest.GetRequestStream(); envelope.Save(requestStream); requestStream.Close(); HttpWebResponse soapResponse = (HttpWebResponse)soapRequest.GetResponse(); if (soapResponse.StatusCode == HttpStatusCode.OK) { // Successful response. Stream responseStream = soapResponse.GetResponseStream(); XElement responseEnvelope = XElement.Load(responseStream); if (responseEnvelope != null) { Tracing.WriteLine("Response:"); PrintElement(responseEnvelope); // If no errors, it is safe to proceed. string error = CheckSoapResponseForError(responseEnvelope); if (error == "NoError") { settingsDictionary = new Dictionary <string, string>(); // Load settings. IEnumerable <XElement> userSettings = from userSetting in responseEnvelope.Descendants (SoapXmlStrings.UserSetting) select userSetting; foreach (XElement userSetting in userSettings) { XElement name = userSetting.Element(SoapXmlStrings.Name); XElement value = userSetting.Element(SoapXmlStrings.Value); if (name != null && value != null) { settingsDictionary.Add(name.Value, value.Value); } } } else if (error == "RedirectAddress") { // The server has given you a better email // address to use. Tracing.WriteLine("Server response contains a RedirectAddress error."); XElement redirectTarget = FindFirstDescendant(responseEnvelope, SoapXmlStrings.RedirectTarget); if (redirectTarget != null && IsValidRedirectAddress(redirectTarget.Value, EmailAddress)) { Tracing.WriteLine("Restarting Autodiscover with email address: " + redirectTarget.Value); EmailAddress = redirectTarget.Value; UrlList.Clear(); UrlList.GenerateList(this.EmailAddress); visitedUrls.Clear(); AddressRedirects++; return(TryAutodiscoverUrls(useSoapEndpoints)); } Tracing.WriteLine("Invalid or missing RedirectTarget element, continuing..."); } else if (error == "RedirectUrl") { // The server has given you a better URL // to use. Validate that it is https and // try it. Tracing.Write("Server response contains a redirect to URL: "); XElement redirectTarget = FindFirstDescendant(responseEnvelope, SoapXmlStrings.RedirectTarget); if (redirectTarget != null && IsValidRedirectUrl(redirectTarget.Value.ToLower())) { Tracing.WriteLine(redirectTarget.Value); UrlRedirects++; return(TryAutodiscoverUrl( AutodiscoverUrlList.NormalizeAutodiscoverUrl(redirectTarget.Value), useSoapEndpoints)); } Tracing.WriteLine("Invalid or missing RedirectTarget element, continuing..."); return(null); } } } else if (soapResponse.StatusCode == HttpStatusCode.Redirect || soapResponse.StatusCode == HttpStatusCode.Moved || soapResponse.StatusCode == HttpStatusCode.RedirectKeepVerb || soapResponse.StatusCode == HttpStatusCode.RedirectMethod) { // Redirect HTTP status scenario. Tracing.WriteLine("Received a redirect status: " + soapResponse.StatusCode.ToString()); string redirectUrl = soapResponse.Headers["Location"].ToString(); Tracing.WriteLine("Location header: " + (string.IsNullOrEmpty(redirectUrl) ? "MISSING" : redirectUrl)); if (IsValidRedirectUrl(redirectUrl.ToLower())) { UrlRedirects++; return(TryAutodiscoverUrl( AutodiscoverUrlList.NormalizeAutodiscoverUrl(redirectUrl), useSoapEndpoints)); } Tracing.WriteLine("Invalid (non-https) redirect URL returned. Unable to proceed."); return(null); } } catch (WebException e) { // Some errors will be exposed as WebExceptions. // For example, 401 (Unauthorized) // 302 should not generate a WebException and is handled above. Tracing.WriteLine("Error connecting:"); Tracing.WriteLine(e.ToString()); } return(settingsDictionary); }