/**
   * Generates the WWW-Authenticate header.
   *
   * <p>The header MUST follow this template :
   *
   * <pre>
   *      WWW-Authenticate    = "WWW-Authenticate" ":" "Digest"
   *                            digest-challenge
   *
   *      digest-challenge    = 1#( realm | [ domain ] | nOnce |
   *                  [ digest-opaque ] |[ stale ] | [ algorithm ] )
   *
   *      realm               = "realm" "=" realm-value
   *      realm-value         = quoted-string
   *      domain              = "domain" "=" <"> 1#URI <">
   *      nonce               = "nonce" "=" nonce-value
   *      nonce-value         = quoted-string
   *      opaque              = "opaque" "=" quoted-string
   *      stale               = "stale" "=" ( "true" | "false" )
   *      algorithm           = "algorithm" "=" ( "MD5" | token )
   * </pre>
   *
   * @param request HTTP Servlet request
   * @param response HTTP Servlet response
   * @param config Login configuration describing how authentication should be performed
   * @param nOnce nonce token
   */
  protected void setAuthenticateHeader(
      SipServletRequestImpl request,
      SipServletResponseImpl response,
      SipLoginConfig config,
      String nOnce) {

    // Get the realm name
    String realmName = config.getRealmName();
    if (realmName == null) realmName = request.getServerName() + ":" + request.getServerPort();

    byte[] buffer = null;
    synchronized (md5Helper) {
      buffer = md5Helper.digest(nOnce.getBytes());
    }

    String authenticateHeader =
        "Digest realm=\""
            + realmName
            + "\", "
            + "qop=\"auth\", nonce=\""
            + nOnce
            + "\", "
            + "opaque=\""
            + md5Encoder.encode(buffer)
            + "\"";

    // There are different headers for different types of auth
    if (response.getStatus() == SipServletResponseImpl.SC_PROXY_AUTHENTICATION_REQUIRED) {
      response.setHeader("Proxy-Authenticate", authenticateHeader);
    } else {
      response.setHeader("WWW-Authenticate", authenticateHeader);
    }
  }
  /**
   * @param inviteTransaction
   * @param inviteRequest
   */
  private static final void send487Response(
      Transaction inviteTransaction, SipServletRequestImpl inviteRequest)
      throws IllegalStateException, DispatcherException {
    SipServletResponseImpl inviteResponse =
        (SipServletResponseImpl) inviteRequest.createResponse(Response.REQUEST_TERMINATED);

    inviteRequest.setRoutingState(RoutingState.CANCELLED);
    // JSR 289 Section 6.2.1.1 Cancel Message Processing : since receiving a CANCEL request causes
    // the UAS
    // to respond to an ongoing INVITE transaction with a non-2XX (specifically, 487) response, the
    // SipSession state
    // normally becomes TERMINATED as a result of the non-2XX final response sent back to the UAC.
    // Issue 1484 : http://code.google.com/p/mobicents/issues/detail?id=1484
    // we terminate the session only for initial requests
    if (inviteRequest.isInitial()) {
      inviteRequest.getSipSession().setState(State.TERMINATED);
    }
    try {
      Response requestTerminatedResponse = (Response) inviteResponse.getMessage();
      ((ServerTransaction) inviteTransaction).sendResponse(requestTerminatedResponse);
    } catch (SipException e) {
      throw new DispatcherException(
          Response.SERVER_INTERNAL_ERROR,
          "Impossible to send the 487 to the INVITE transaction corresponding to CANCEL",
          e);
    } catch (InvalidArgumentException e) {
      throw new DispatcherException(
          Response.SERVER_INTERNAL_ERROR,
          "Impossible to send the 487 to the INVITE transaction corresponding to CANCEL",
          e);
    }
  }
  /**
   * Authenticate the user making this request, based on the specified login configuration. Return
   * <code>true</code> if any specified constraint has been satisfied, or <code>false</code> if we
   * have created a response challenge already.
   *
   * @param request Request we are processing
   * @param response Response we are creating
   * @param config Login configuration describing how authentication should be performed
   * @exception IOException if an input/output error occurs
   */
  public boolean authenticate(
      SipServletRequestImpl request, SipServletResponseImpl response, SipLoginConfig config)
      throws IOException {

    principal = null;

    // Have we already authenticated someone?
    principal = request.getUserPrincipal();
    // String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
    if (principal != null) {
      if (log.isDebugEnabled()) log.debug("Already authenticated '" + principal.getName() + "'");

      return (true);
    }

    // Validate any credentials already included with this request
    String authorization = request.getHeader("authorization");
    if (authorization != null) {
      principal = findPrincipal(request, authorization, context.getRealm());
      if (principal != null) {
        String username = parseUsername(authorization);
        register(request, response, principal, Constants.DIGEST_METHOD, username, null);
        return (true);
      }
    }

    // Send an "unauthorized" response and an appropriate challenge

    // Next, generate a nOnce token (that is a token which is supposed
    // to be unique).
    String nOnce = generateNOnce(request);

    setAuthenticateHeader(request, response, config, nOnce);
    response.send();
    //      hres.flushBuffer();
    return (false);
  }
  /** {@inheritDoc} */
  public void dispatchMessage(
      final SipProvider sipProvider, SipServletMessageImpl sipServletMessage)
      throws DispatcherException {
    //		final SipNetworkInterfaceManager sipNetworkInterfaceManager =
    // sipApplicationDispatcher.getSipNetworkInterfaceManager();
    final SipServletRequestImpl sipServletRequest = (SipServletRequestImpl) sipServletMessage;
    if (logger.isInfoEnabled()) {
      logger.info("Routing of Cancel Request " + sipServletRequest);
    }
    /*
     * WARNING: routing of CANCEL is special because CANCEL does not contain Route headers as other requests related
     * to the dialog. But still it has to be routed through the app path
     * of the INVITE
     */
    /* If there is a proxy with the request, let's try to send it directly there.
     * This is needed because of CANCEL which is a subsequent request that might
     * not have Routes. For example if the callee has'n responded the caller still
     * doesn't know the route-record and just sends cancel to the outbound proxy.
     */
    //		boolean proxyCancel = false;
    ServerTransaction cancelTransaction = (ServerTransaction) sipServletRequest.getTransaction();

    if (cancelTransaction != null
        && !TransactionState.TERMINATED.equals(cancelTransaction.getState())) {
      if (logger.isDebugEnabled()) {
        logger.debug("Sending 200 to Cancel " + sipServletRequest);
      }
      try {
        // First we need to send OK ASAP because of retransmissions both for
        // proxy or app
        SipServletResponseImpl cancelResponse =
            (SipServletResponseImpl) sipServletRequest.createResponse(200, "Canceling");
        Response cancelJsipResponse = (Response) cancelResponse.getMessage();
        cancelTransaction.sendResponse(cancelJsipResponse);
      } catch (SipException e) {
        throw new DispatcherException(
            Response.SERVER_INTERNAL_ERROR, "Impossible to send the ok to the CANCEL", e);
      } catch (InvalidArgumentException e) {
        throw new DispatcherException(
            Response.SERVER_INTERNAL_ERROR, "Impossible to send the ok to the CANCEL", e);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("checking what to do with the CANCEL " + sipServletRequest);
      }
      DispatchTask dispatchTask = new CancelDispatchTask(sipServletRequest, sipProvider);
      // Execute CANCEL without waiting for previous requests because if we wait for an INVITE to
      // complete
      // all responses will be already sent by the time the CANCEL is out of the queue.
      this.sipApplicationDispatcher.getAsynchronousExecutor().execute(dispatchTask);
    } else {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Retransmission received for CANCEL "
                + sipServletRequest
                + ", transaction "
                + cancelTransaction);
        if (cancelTransaction != null) {
          logger.debug("Cancel Transaction state " + cancelTransaction.getState());
        }
      }
    }
  }