/**
   * Process a type 1 NTLM message
   *
   * @param type1Msg Type1NTLMMessage
   * @param req HttpServletRequest
   * @param res HttpServletResponse
   * @exception IOException
   */
  protected void processType1(
      Type1NTLMMessage type1Msg, HttpServletRequest req, HttpServletResponse res)
      throws IOException {
    if (getLogger().isDebugEnabled()) getLogger().debug("Received type1 " + type1Msg);

    // Get the existing NTLM details
    NTLMLogonDetails ntlmDetails = null;
    HttpSession session = req.getSession();
    ntlmDetails = (NTLMLogonDetails) session.getAttribute(NTLM_AUTH_DETAILS);

    // Check if cached logon details are available
    if (ntlmDetails != null
        && ntlmDetails.hasType2Message()
        && ntlmDetails.hasNTLMHashedPassword()
        && ntlmDetails.hasAuthenticationToken()) {
      // Get the authentication server type2 response
      Type2NTLMMessage cachedType2 = ntlmDetails.getType2Message();

      byte[] type2Bytes = cachedType2.getBytes();
      String ntlmBlob = "NTLM " + new String(Base64.encodeBase64(type2Bytes));

      if (getLogger().isDebugEnabled())
        getLogger().debug("Sending cached NTLM type2 to client - " + cachedType2);

      // Send back a request for NTLM authentication
      res.setHeader(WWW_AUTHENTICATE, ntlmBlob);
      res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      res.flushBuffer();
    } else {
      // Clear any cached logon details
      session.removeAttribute(NTLM_AUTH_DETAILS);

      // Set the 8 byte challenge for the new logon request
      byte[] challenge = null;
      NTLMPassthruToken authToken = null;

      if (nltmAuthenticator.getNTLMMode() == NTLMMode.MD4_PROVIDER) {
        // Generate a random 8 byte challenge

        challenge = new byte[8];
        DataPacker.putIntelLong(m_random.nextLong(), challenge, 0);
      } else {
        // Get the client domain
        String domain = type1Msg.getDomain();
        if (domain == null || domain.length() == 0) {
          domain = mapClientAddressToDomain(req.getRemoteAddr());
        }

        if (getLogger().isDebugEnabled()) getLogger().debug("Client domain " + domain);

        // Create an authentication token for the new logon
        authToken = new NTLMPassthruToken(domain);

        // Run the first stage of the passthru authentication to get the challenge
        nltmAuthenticator.authenticate(authToken);

        // Get the challenge from the token
        if (authToken.getChallenge() != null) {
          challenge = authToken.getChallenge().getBytes();
        }
      }

      // Get the flags from the client request and mask out unsupported features
      int ntlmFlags = type1Msg.getFlags() & m_ntlmFlags;

      // Build a type2 message to send back to the client, containing the challenge
      List<TargetInfo> tList = new ArrayList<TargetInfo>();
      String srvName = getServerName();
      tList.add(new TargetInfo(NTLM.TargetServer, srvName));

      Type2NTLMMessage type2Msg = new Type2NTLMMessage();
      type2Msg.buildType2(ntlmFlags, srvName, challenge, null, tList);

      // Store the NTLM logon details, cache the type2 message, and token if using passthru
      ntlmDetails = new NTLMLogonDetails();
      ntlmDetails.setType2Message(type2Msg);
      ntlmDetails.setAuthenticationToken(authToken);

      session.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails);

      if (getLogger().isDebugEnabled())
        getLogger().debug("Sending NTLM type2 to client - " + type2Msg);

      // Send back a request for NTLM authentication
      byte[] type2Bytes = type2Msg.getBytes();
      String ntlmBlob = "NTLM " + new String(Base64.encodeBase64(type2Bytes));

      res.setHeader(WWW_AUTHENTICATE, ntlmBlob);
      res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      res.flushBuffer();
    }
  }