/**
  * * Read SOAP body.
  *
  * @param reader EwsXmlReader.
  * @return AutodiscoverResponse AutodiscoverResponse object
  * @throws InstantiationException the instantiation exception
  * @throws IllegalAccessException the illegal access exception
  * @throws ParseException the parse exception
  * @throws Exception the exception
  */
 protected AutodiscoverResponse readSoapBody(EwsXmlReader reader)
     throws InstantiationException, IllegalAccessException, ParseException, Exception {
   reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);
   AutodiscoverResponse responses = this.loadFromXml(reader);
   reader.readEndElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);
   return responses;
 }
  /**
   * * Read SOAP header.
   *
   * @param reader EwsXmlReader.
   * @throws Exception the exception
   */
  protected void readSoapHeader(EwsXmlReader reader) throws Exception {
    reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName);
    do {
      reader.read();

      // Is this the ServerVersionInfo?
      if (reader.isStartElement(XmlNamespace.Autodiscover, XmlElementNames.ServerVersionInfo)) {
        this.service.setServerInfo(this.readServerVersionInfo(reader));
      }
    } while (!reader.isEndElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName));
  }
 /**
  * * Loads responses from XML.
  *
  * @param reader The reader.
  * @return AutodiscoverResponse AutodiscoverResponse object
  * @throws InstantiationException the instantiation exception
  * @throws IllegalAccessException the illegal access exception
  * @throws ParseException the parse exception
  * @throws Exception the exception
  */
 protected AutodiscoverResponse loadFromXml(EwsXmlReader reader)
     throws InstantiationException, IllegalAccessException, ParseException, Exception {
   String elementName = this.getResponseXmlElementName();
   reader.readStartElement(XmlNamespace.Autodiscover, elementName);
   AutodiscoverResponse response = this.createServiceResponse();
   response.loadFromXml(reader, elementName);
   return response;
 }
  /**
   * Read user setting with ProtocolConnection value.
   *
   * @param reader EwsServiceXmlReader
   * @return the protocol connection
   * @throws Exception the exception
   */
  protected static ProtocolConnection loadFromXml(EwsXmlReader reader) throws Exception {
    ProtocolConnection connection = new ProtocolConnection();

    do {
      reader.read();

      if (reader.getNodeType().getNodeType() == XmlNodeType.START_ELEMENT) {
        if (reader.getLocalName().equals(XmlElementNames.EncryptionMethod)) {
          connection.setEncryptionMethod(reader.readElementValue(String.class));
        } else if (reader.getLocalName().equals(XmlElementNames.Hostname)) {
          connection.setHostname(reader.readElementValue(String.class));
        } else if (reader.getLocalName().equals(XmlElementNames.Port)) {
          connection.setPort(reader.readElementValue(int.class));
        }
      }
    } while (!reader.isEndElement(XmlNamespace.Autodiscover, XmlElementNames.ProtocolConnection));

    return connection;
  }
  /**
   * * Executes this instance.
   *
   * @return the autodiscover response
   * @throws ServiceLocalException the service local exception
   * @throws Exception the exception
   */
  protected AutodiscoverResponse internalExecute() throws ServiceLocalException, Exception {
    this.validate();
    HttpWebRequest request = null;
    try {
      request = this.service.prepareHttpWebRequestForUrl(this.url);
      this.service.traceHttpRequestHeaders(TraceFlags.AutodiscoverRequestHttpHeaders, request);

      OutputStream urlOutStream = request.getOutputStream();
      // OutputStreamWriter out = new OutputStreamWriter(request
      // .getOutputStream());

      ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
      this.writeSoapRequest(this.url, memoryStream);

      if (this.service.isTraceEnabledFor(TraceFlags.AutodiscoverRequest)) {
        memoryStream.flush();
        this.service.traceXml(TraceFlags.AutodiscoverRequest, memoryStream);
      }
      memoryStream.writeTo(urlOutStream);
      urlOutStream.flush();
      urlOutStream.close();
      memoryStream.close();
      // out.write(memoryStream.toString());
      // out.close();
      request.executeRequest();
      request.getResponseCode();
      if (AutodiscoverRequest.isRedirectionResponse(request)) {
        AutodiscoverResponse response = this.createRedirectionResponse(request);
        if (response != null) {
          return response;
        } else {
          throw new ServiceRemoteException(Strings.InvalidRedirectionResponseReturned);
        }
      }

      /*
       * BufferedReader in = new BufferedReader(new
       * InputStreamReader(request.getInputStream()));
       *
       * String decodedString;
       *
       * while ((decodedString = in.readLine()) != null) {
       * System.out.println(decodedString); } in.close();
       */

      memoryStream = new ByteArrayOutputStream();
      InputStream serviceResponseStream = request.getInputStream();

      while (true) {
        int data = serviceResponseStream.read();
        if (-1 == data) {
          break;
        } else {
          memoryStream.write(data);
        }
      }
      memoryStream.flush();
      serviceResponseStream.close();

      if (this.service.isTraceEnabled()) {
        this.service.traceResponse(request, memoryStream);
      }
      ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(memoryStream.toByteArray());
      EwsXmlReader ewsXmlReader = new EwsXmlReader(memoryStreamIn);

      // WCF may not generate an XML declaration.
      ewsXmlReader.read();
      if (ewsXmlReader.getNodeType().getNodeType() == XMLNodeType.START_DOCUMENT) {
        ewsXmlReader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
      } else if ((ewsXmlReader.getNodeType().getNodeType() != XMLNodeType.START_ELEMENT)
          || (!ewsXmlReader.getLocalName().equals(XmlElementNames.SOAPEnvelopeElementName))
          || (!ewsXmlReader
              .getNamespaceUri()
              .equals(EwsUtilities.getNamespaceUri(XmlNamespace.Soap)))) {
        throw new ServiceXmlDeserializationException(Strings.InvalidAutodiscoverServiceResponse);
      }

      this.readSoapHeader(ewsXmlReader);

      AutodiscoverResponse response = this.readSoapBody(ewsXmlReader);

      ewsXmlReader.readEndElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);

      if (response.getErrorCode() == AutodiscoverErrorCode.NoError) {
        return response;
      } else {
        throw new AutodiscoverResponseException(
            response.getErrorCode(), response.getErrorMessage());
      }

    } catch (XMLStreamException ex) {
      this.service.traceMessage(
          TraceFlags.AutodiscoverConfiguration,
          String.format("XML parsing error: %s", ex.getMessage()));

      // Wrap exception
      throw new ServiceRequestException(
          String.format(Strings.ServiceRequestFailed, ex.getMessage()), ex);
    } catch (IOException ex) {
      this.service.traceMessage(
          TraceFlags.AutodiscoverConfiguration, String.format("I/O error: %s", ex.getMessage()));

      // Wrap exception
      throw new ServiceRequestException(
          String.format(Strings.ServiceRequestFailed, ex.getMessage()), ex);
    } catch (Exception ex) {
      // HttpWebRequest httpWebResponse = (HttpWebRequest)ex;

      if (null != request && request.getResponseCode() == 7) {
        if (AutodiscoverRequest.isRedirectionResponse(request)) {
          this.service.traceHttpResponseHeaders(
              TraceFlags.AutodiscoverResponseHttpHeaders, request);

          AutodiscoverResponse response = this.createRedirectionResponse(request);
          if (response != null) {
            return response;
          }
        } else {
          this.processWebException(ex, request);
        }
      }

      // Wrap exception if the above code block didn't throw
      throw new ServiceRequestException(
          String.format(Strings.ServiceRequestFailed, ex.getMessage()), ex);
    } finally {
      try {
        request.close();
      } catch (Exception e2) {
        request = null;
      }
    }
  }
  /**
   * * Read ServerVersionInfo SOAP header.
   *
   * @param reader EwsXmlReader.
   * @return ExchangeServerInfo ExchangeServerInfo object
   * @throws Exception the exception
   */
  private ExchangeServerInfo readServerVersionInfo(EwsXmlReader reader) throws Exception {
    ExchangeServerInfo serverInfo = new ExchangeServerInfo();
    do {
      reader.read();

      if (reader.isStartElement()) {
        if (reader.getLocalName().equals(XmlElementNames.MajorVersion)) {
          serverInfo.setMajorVersion(reader.readElementValue(Integer.class));
        } else if (reader.getLocalName().equals(XmlElementNames.MinorVersion)) {
          serverInfo.setMinorVersion(reader.readElementValue(Integer.class));
        } else if (reader.getLocalName().equals(XmlElementNames.MajorBuildNumber)) {
          serverInfo.setMajorBuildNumber(reader.readElementValue(Integer.class));
        } else if (reader.getLocalName().equals(XmlElementNames.MinorBuildNumber)) {
          serverInfo.setMinorBuildNumber(reader.readElementValue(Integer.class));
        } else if (reader.getLocalName().equals(XmlElementNames.Version)) {
          serverInfo.setVersionString(reader.readElementValue());
        }
      }
    } while (!reader.isEndElement(XmlNamespace.Autodiscover, XmlElementNames.ServerVersionInfo));

    return serverInfo;
  }
  /**
   * * Reads the SOAP fault.
   *
   * @param reader The reader.
   * @return SOAP fault details.
   */
  private SoapFaultDetails readSoapFault(EwsXmlReader reader) {
    SoapFaultDetails soapFaultDetails = null;

    try {

      reader.read();
      if (reader.getNodeType().getNodeType() == XMLNodeType.START_DOCUMENT) {
        reader.read();
      }
      if (!reader.isStartElement()
          || (!reader.getLocalName().equals(XmlElementNames.SOAPEnvelopeElementName))) {
        return soapFaultDetails;
      }

      // Get the namespace URI from the envelope element and use it for
      // the rest of the parsing.
      // If it's not 1.1 or 1.2, we can't continue.
      XmlNamespace soapNamespace = EwsUtilities.getNamespaceFromUri(reader.getNamespaceUri());
      if (soapNamespace == XmlNamespace.NotSpecified) {
        return soapFaultDetails;
      }

      reader.read();

      // Skip SOAP header.
      if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPHeaderElementName)) {
        do {
          reader.read();
        } while (!reader.isEndElement(soapNamespace, XmlElementNames.SOAPHeaderElementName));

        // Queue up the next read
        reader.read();
      }

      // Parse the fault element contained within the SOAP body.
      if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPBodyElementName)) {
        do {
          reader.read();

          // Parse Fault element
          if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPFaultElementName)) {
            soapFaultDetails = SoapFaultDetails.parse(reader, soapNamespace);
          }
        } while (!reader.isEndElement(soapNamespace, XmlElementNames.SOAPBodyElementName));
      }

      reader.readEndElement(soapNamespace, XmlElementNames.SOAPEnvelopeElementName);
    } catch (Exception e) {
      // If response doesn't contain a valid SOAP fault, just ignore
      // exception and
      // return null for SOAP fault details.
      e.printStackTrace();
    }

    return soapFaultDetails;
  }