/**
  * Tests if the {@link HttpMethod method} requires authentication.
  *
  * @param method HTTP method
  * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
  */
 private boolean isAuthenticationNeeded(final HttpMethod method) {
   method
       .getHostAuthState()
       .setAuthRequested(method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED);
   method
       .getProxyAuthState()
       .setAuthRequested(method.getStatusCode() == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
   if (method.getHostAuthState().isAuthRequested()
       || method.getProxyAuthState().isAuthRequested()) {
     LOG.debug("Authorization required");
     if (method.getDoAuthentication()) { // process authentication response
       return true;
     } else { // let the client handle the authenticaiton
       LOG.info("Authentication requested but doAuthentication is " + "disabled");
       return false;
     }
   } else {
     return false;
   }
 }
 private HTTPClientResponseResolver createResponseResolver(
     final HttpMethod httpMethod, final Status status, final Header[] headers) {
   try {
     when(httpMethod.getStatusLine())
         .thenReturn(
             new org.apache.commons.httpclient.StatusLine(
                 String.format("HTTP/1.1 %s %s\r\n", status.getCode(), status.getName())));
   } catch (HttpException e) {
     throw new RuntimeException(e);
   }
   when(httpMethod.getStatusCode()).thenReturn(status.getCode());
   when(httpMethod.getResponseHeaders()).thenReturn(headers);
   return new TestableHTTPClientResponseResolver(httpMethod);
 }
 /**
  * Tests if the {@link HttpMethod method} requires a redirect to another location.
  *
  * @param method HTTP method
  * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
  */
 private boolean isRedirectNeeded(final HttpMethod method) {
   switch (method.getStatusCode()) {
     case HttpStatus.SC_MOVED_TEMPORARILY:
     case HttpStatus.SC_MOVED_PERMANENTLY:
     case HttpStatus.SC_SEE_OTHER:
     case HttpStatus.SC_TEMPORARY_REDIRECT:
       LOG.debug("Redirect required");
       if (method.getFollowRedirects()) {
         return true;
       } else {
         return false;
       }
     default:
       return false;
   } // end of switch
 }
  /**
   * Processes a response that requires authentication
   *
   * @param method the current {@link HttpMethod HTTP method}
   * @return <tt>true</tt> if the authentication challenge can be responsed to, (that is, at least
   *     one of the requested authentication scheme is supported, and matching credentials have been
   *     found), <tt>false</tt> otherwise.
   */
  private boolean processAuthenticationResponse(final HttpMethod method) {
    LOG.trace("enter HttpMethodBase.processAuthenticationResponse(" + "HttpState, HttpConnection)");

    try {
      switch (method.getStatusCode()) {
        case HttpStatus.SC_UNAUTHORIZED:
          return processWWWAuthChallenge(method);
        case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
          return processProxyAuthChallenge(method);
        default:
          return false;
      }
    } catch (Exception e) {
      if (LOG.isErrorEnabled()) {
        LOG.error(e.getMessage(), e);
      }
      return false;
    }
  }
  /**
   * Makes a rest request of any type at the specified urlSuffix. The urlSuffix should be of the
   * form /userService/users. If CS throws an exception it handled and transalated to a Openfire
   * exception if possible. This is done using the check fault method that tries to throw the best
   * maching exception.
   *
   * @param type Must be GET or DELETE
   * @param urlSuffix The url suffix of the rest request
   * @param xmlParams The xml with the request params, must be null if type is GET or DELETE only
   * @return The response as a xml doc.
   * @throws ConnectionException Thrown if there are issues perfoming the request.
   * @throws Exception Thrown if the response from Clearspace contains an exception.
   */
  public Element executeRequest(HttpType type, String urlSuffix, String xmlParams)
      throws ConnectionException, Exception {
    if (Log.isDebugEnabled()) {
      Log.debug("Outgoing REST call [" + type + "] to " + urlSuffix + ": " + xmlParams);
    }

    String wsUrl = getConnectionURI() + WEBSERVICES_PATH + urlSuffix;

    String secret = getSharedSecret();

    HttpClient client = new HttpClient();
    HttpMethod method;

    // Configures the authentication
    client.getParams().setAuthenticationPreemptive(true);
    Credentials credentials = new UsernamePasswordCredentials(OPENFIRE_USERNAME, secret);
    AuthScope scope = new AuthScope(host, port, AuthScope.ANY_REALM);
    client.getState().setCredentials(scope, credentials);

    // Creates the method
    switch (type) {
      case GET:
        method = new GetMethod(wsUrl);
        break;
      case POST:
        PostMethod pm = new PostMethod(wsUrl);
        StringRequestEntity requestEntity = new StringRequestEntity(xmlParams);
        pm.setRequestEntity(requestEntity);
        method = pm;
        break;
      case PUT:
        PutMethod pm1 = new PutMethod(wsUrl);
        StringRequestEntity requestEntity1 = new StringRequestEntity(xmlParams);
        pm1.setRequestEntity(requestEntity1);
        method = pm1;
        break;
      case DELETE:
        method = new DeleteMethod(wsUrl);
        break;
      default:
        throw new IllegalArgumentException();
    }

    method.setRequestHeader("Accept", "text/xml");
    method.setDoAuthentication(true);

    try {
      // Executes the request
      client.executeMethod(method);

      // Parses the result
      String body = method.getResponseBodyAsString();
      if (Log.isDebugEnabled()) {
        Log.debug("Outgoing REST call results: " + body);
      }

      // Checks the http status
      if (method.getStatusCode() != 200) {
        if (method.getStatusCode() == 401) {
          throw new ConnectionException(
              "Invalid password to connect to Clearspace.",
              ConnectionException.ErrorType.AUTHENTICATION);
        } else if (method.getStatusCode() == 404) {
          throw new ConnectionException(
              "Web service not found in Clearspace.", ConnectionException.ErrorType.PAGE_NOT_FOUND);
        } else if (method.getStatusCode() == 503) {
          throw new ConnectionException(
              "Web service not avaible in Clearspace.",
              ConnectionException.ErrorType.SERVICE_NOT_AVAIBLE);
        } else {
          throw new ConnectionException(
              "Error connecting to Clearspace, http status code: " + method.getStatusCode(),
              new HTTPConnectionException(method.getStatusCode()),
              ConnectionException.ErrorType.OTHER);
        }
      } else if (body.contains("Clearspace Upgrade Console")) {
        // TODO Change CS to send a more standard error message
        throw new ConnectionException(
            "Clearspace is in an update state.", ConnectionException.ErrorType.UPDATE_STATE);
      }

      Element response = localParser.get().parseDocument(body).getRootElement();

      // Check for exceptions
      checkFault(response);

      // Since there is no exception, returns the response
      return response;
    } catch (DocumentException e) {
      throw new ConnectionException(
          "Error parsing the response of Clearspace.", e, ConnectionException.ErrorType.OTHER);
    } catch (HttpException e) {
      throw new ConnectionException(
          "Error performing http request to Clearspace", e, ConnectionException.ErrorType.OTHER);
    } catch (UnknownHostException e) {
      throw new ConnectionException(
          "Unknown Host " + getConnectionURI() + " trying to connect to Clearspace",
          e,
          ConnectionException.ErrorType.UNKNOWN_HOST);
    } catch (IOException e) {
      throw new ConnectionException(
          "Error peforming http request to Clearspace.", e, ConnectionException.ErrorType.OTHER);
    } finally {
      method.releaseConnection();
    }
  }
  /**
   * Process the redirect response.
   *
   * @return <code>true</code> if the redirect was successful
   */
  private boolean processRedirectResponse(final HttpMethod method) throws RedirectException {
    // get the location header to find out where to redirect to
    Header locationHeader = method.getResponseHeader("location");
    if (locationHeader == null) {
      // got a redirect response, but no location header
      LOG.error("Received redirect response " + method.getStatusCode() + " but no location header");
      return false;
    }
    String location = locationHeader.getValue();
    if (LOG.isDebugEnabled()) {
      LOG.debug("Redirect requested to location '" + location + "'");
    }

    // rfc2616 demands the location value be a complete URI
    // Location       = "Location" ":" absoluteURI
    URI redirectUri = null;
    URI currentUri = null;

    try {
      currentUri =
          new URI(
              this.conn.getProtocol().getScheme(),
              null,
              this.conn.getHost(),
              this.conn.getPort(),
              method.getPath());

      String charset = method.getParams().getUriCharset();
      redirectUri = new URI(location, true, charset);

      if (redirectUri.isRelativeURI()) {
        if (this.params.isParameterTrue(HttpClientParams.REJECT_RELATIVE_REDIRECT)) {
          LOG.warn("Relative redirect location '" + location + "' not allowed");
          return false;
        } else {
          // location is incomplete, use current values for defaults
          LOG.debug("Redirect URI is not absolute - parsing as relative");
          redirectUri = new URI(currentUri, redirectUri);
        }
      } else {
        // Reset the default params
        method.getParams().setDefaults(this.params);
      }
      method.setURI(redirectUri);
      hostConfiguration.setHost(redirectUri);
    } catch (URIException ex) {
      throw new InvalidRedirectLocationException(
          "Invalid redirect location: " + location, location, ex);
    }

    if (this.params.isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) {
      if (this.redirectLocations == null) {
        this.redirectLocations = new HashSet();
      }
      this.redirectLocations.add(currentUri);
      try {
        if (redirectUri.hasQuery()) {
          redirectUri.setQuery(null);
        }
      } catch (URIException e) {
        // Should never happen
        return false;
      }

      if (this.redirectLocations.contains(redirectUri)) {
        throw new CircularRedirectException("Circular redirect to '" + redirectUri + "'");
      }
    }

    if (LOG.isDebugEnabled()) {
      LOG.debug(
          "Redirecting from '"
              + currentUri.getEscapedURI()
              + "' to '"
              + redirectUri.getEscapedURI());
    }
    // And finally invalidate the actual authentication scheme
    method.getHostAuthState().invalidate();
    return true;
  }