/* ------------------------------------------------------------ */
  private int checkNonce(String nonce, Request request) {
    try {
      byte[] n = B64Code.decode(nonce.toCharArray());
      if (n.length != 24) {
        return -1;
      }

      long ts = 0;
      long sk = _nonceSecret;
      byte[] n2 = new byte[16];
      System.arraycopy(n, 0, n2, 0, 8);
      for (int i = 0; i < 8; i++) {
        n2[8 + i] = (byte) (sk & 0xff);
        sk = sk >> 8;
        ts = (ts << 8) + (0xff & (long) n[7 - i]);
      }

      long age = request.getTimeStamp() - ts;
      if (Log.isDebugEnabled()) {
        Log.debug("age=" + age);
      }

      byte[] hash = null;
      try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.reset();
        md.update(n2, 0, 16);
        hash = md.digest();
      } catch (Exception e) {
        Log.warn(e);
      }

      for (int i = 0; i < 16; i++) {
        if (n[i + 8] != hash[i]) {
          return -1;
        }
      }

      if (_maxNonceAge > 0 && (age < 0 || age > _maxNonceAge)) {
        return 0; // stale
      }

      return 1;
    } catch (Exception e) {
      Log.ignore(e);
    }
    return -1;
  }
  public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory)
      throws ServerAuthException {
    if (!mandatory) {
      return _deferred;
    }

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    String credentials = request.getHeader(HttpHeaders.AUTHORIZATION);

    try {
      boolean stale = false;
      if (credentials != null) {
        if (Log.isDebugEnabled()) {
          Log.debug("Credentials: " + credentials);
        }
        QuotedStringTokenizer tokenizer =
            new QuotedStringTokenizer(credentials, "=, ", true, false);
        final Digest digest = new Digest(request.getMethod());
        String last = null;
        String name = null;

        while (tokenizer.hasMoreTokens()) {
          String tok = tokenizer.nextToken();
          char c = (tok.length() == 1) ? tok.charAt(0) : '\0';

          switch (c) {
            case '=':
              name = last;
              last = tok;
              break;
            case ',':
              name = null;
            case ' ':
              break;

            default:
              last = tok;
              if (name != null) {
                if ("username".equalsIgnoreCase(name)) {
                  digest.username = tok;
                } else if ("realm".equalsIgnoreCase(name)) {
                  digest.realm = tok;
                } else if ("nonce".equalsIgnoreCase(name)) {
                  digest.nonce = tok;
                } else if ("nc".equalsIgnoreCase(name)) {
                  digest.nc = tok;
                } else if ("cnonce".equalsIgnoreCase(name)) {
                  digest.cnonce = tok;
                } else if ("qop".equalsIgnoreCase(name)) {
                  digest.qop = tok;
                } else if ("uri".equalsIgnoreCase(name)) {
                  digest.uri = tok;
                } else if ("response".equalsIgnoreCase(name)) {
                  digest.response = tok;
                }
                break;
              }
          }
        }

        int n = checkNonce(digest.nonce, (Request) request);

        if (n > 0) {
          UserIdentity user = _loginService.login(digest.username, digest);
          if (user != null) {
            return new UserAuthentication(getAuthMethod(), user);
          }
        } else if (n == 0) {
          stale = true;
        }
      }

      if (!_deferred.isDeferred(response)) {
        String domain = request.getContextPath();
        if (domain == null) {
          domain = "/";
        }
        response.setHeader(
            HttpHeaders.WWW_AUTHENTICATE,
            "Digest realm=\""
                + _loginService.getName()
                + "\", domain=\""
                + domain
                + "\", nonce=\""
                + newNonce((Request) request)
                + "\", algorithm=MD5, qop=\"auth\""
                + (_useStale ? (" stale=" + stale) : ""));
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        return Authentication.SEND_CONTINUE;
      }

      return Authentication.UNAUTHENTICATED;
    } catch (Exception e) {
      throw new ServerAuthException(e);
    }
  }