protected boolean shouldIgnoreBCCAddress(String address) {
   MilterServerService milterService = Config.getConfig().getMilterServerService();
   List<String> ignoreAddresses = milterService.getIgnoreBCCAddress();
   Matcher m = headerPattern2.matcher(address.toLowerCase(Locale.ENGLISH).trim());
   if (m.matches()) {
     String mailAddress = m.group(1);
     for (String ignoreAddress : ignoreAddresses) {
       if (ignoreAddress.equalsIgnoreCase(mailAddress)) return true;
     }
   } else {
     m = headerPattern3.matcher(address.toLowerCase(Locale.ENGLISH).trim());
     if (m.matches()) {
       String mailAddress = m.group(1);
       for (String ignoreAddress : ignoreAddresses) {
         if (ignoreAddress.equalsIgnoreCase(mailAddress)) return true;
       }
     }
   }
   return false;
 }
  public JilterStatus header(String headerf, String headerv) {

    logger.debug("jilter header {name='" + headerf + "',value='" + headerv + "'}");
    StringBuffer header = new StringBuffer();
    header.append(headerf);
    header.append(": ");
    header.append(headerv);
    header.append("\n");
    try {
      bos.write(header.toString().getBytes());
    } catch (IOException io) {
      logger.error("jilter failed to write header field", io);
    }
    Matcher m = headerPattern1.matcher(headerf.toLowerCase(Locale.ENGLISH).trim());
    if (m.matches()) {
      logger.debug("jilter found to/bcc/cc header");
      String[] addresses = headerv.split(",");
      for (int i = 0; i < addresses.length; i++) {
        includeBCC = includeBCC | rcpts.remove(addresses[i].toLowerCase(Locale.ENGLISH).trim());
        logger.debug("jilter del recipient {recipient='" + addresses[i] + "'}");
        m = headerPattern2.matcher(addresses[i].toLowerCase(Locale.ENGLISH).trim());
        if (m.matches()) {
          String mailAddress = m.group(1);
          includeBCC = includeBCC | rcpts.remove(mailAddress);
          logger.debug("jilter del recipient {recipient='" + mailAddress + "'}");
        } else {
          m = headerPattern3.matcher(addresses[i].toLowerCase(Locale.ENGLISH).trim());
          if (m.matches()) {
            String mailAddress = m.group(1);
            includeBCC = includeBCC | rcpts.remove(mailAddress);
            logger.debug("jilter del recipient {recipient='" + mailAddress + "'}");
          }
        }
      }
    }
    return JilterStatus.SMFIS_CONTINUE;
  }
public class MilterRequestHandler implements RequestHandler, JilterHandler, StopBlockTarget {

  protected static Log logger = LogFactory.getLog(MilterRequestHandler.class.getName());
  protected SocketChannel socket = null;
  protected ArrayList<String> rcpts = null;
  protected FetchMessageCallback callback;
  protected String host = "";
  protected JilterStatus status = null;
  protected ByteArrayOutputStream bos = new ByteArrayOutputStream();
  protected static Pattern headerPattern1 = Pattern.compile("^cc|^to|^bcc");
  protected static Pattern headerPattern2 = Pattern.compile(".*<([-.+_\\d\\w]*@[-.+_\\d\\w]*)>");
  protected static Pattern headerPattern3 = Pattern.compile("([-.+_\\d\\w]*@[-.+_\\d\\w]*)");
  private static final int IDLE_TIMEOUT = 300000; // 5 minutes
  protected boolean includeBCC = false;

  public void handleRequest(SocketChannel socket, FetchMessageCallback callback) {
    this.socket = socket;
    this.callback = callback;
    includeBCC = false;
    rcpts = new ArrayList<String>();
    bos = new ByteArrayOutputStream();

    InetAddress address = socket.socket().getInetAddress();
    boolean isAllowed = Config.getConfig().getAgent().isAllowed(address);
    if (!isAllowed) {
      logger.debug(
          "attempted milter connection from disallowed address. force disconnect {address='"
              + address.getHostAddress()
              + "'}");
      try {
        socket.close();
      } catch (IOException io) {
        logger.error("failed to close milter socket.", io);
      }
      return;
    }

    ByteBuffer dataBuffer = ByteBuffer.allocateDirect(4096);
    JilterProcessor processor = new JilterProcessor(this);
    try {
      while (processor.process(socket, (ByteBuffer) dataBuffer.flip())) {
        dataBuffer.compact();
        if (this.socket.read(dataBuffer) == -1) {
          logger.debug("socket reports EOF, exiting read loop");
          break;
        }
      }
    } catch (IOException e) {
      logger.debug("Unexpected exception, connection will be closed", e);
    } finally {
      logger.debug("closing processor");
      processor.close();
      logger.debug("processor closed");
      try {
        logger.debug("closing socket");
        this.socket.close();
        logger.debug("socket closed");
      } catch (IOException e) {
        logger.debug("Unexpected exception", e);
      }
    }
  }

  public JilterStatus abort() {
    logger.debug("abort");
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus body(ByteBuffer bodyp) {
    logger.debug("jilter body()");
    long maxMessageSizeMB = Config.getConfig().getArchiver().getMaxMessageSize();
    long maxMessageSizeBytes = maxMessageSizeMB * 1024 * 1024;
    if (bodyp.array().length > maxMessageSizeBytes) {
      logger.warn(
          "milter maximum message size exceeded { size='" + bodyp.array().length + " bytes'}");
      return JilterStatus.SMFIS_REJECT;
    }
    try {
      bos.write("\n".getBytes());
      bos.write(bodyp.array());
    } catch (IOException io) {
      logger.error("jilter failed to write milter body data to byte buffer", io);
    }
    logger.debug("jilter body written");
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus close() {
    logger.debug("jilter close()");
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus connect(String hostname, InetAddress hostaddr, Properties properties) {
    rcpts = new ArrayList<String>();
    if (hostaddr != null) {
      host = hostaddr.toString();
    } else if (host != null) {
      host = hostname;
    } else {
      host = "localhost";
    }
    logger.debug("jilter connect() {from='" + hostname + "',host='" + host + "'}");
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus envfrom(String[] argv, Properties properties) {
    for (int i = 0; i < argv.length; i++) {
      logger.debug("jilter envfrom() {from='" + argv[i] + "'}");
    }
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus envrcpt(String[] argv, Properties properties) {
    for (int i = 0; i < argv.length; i++) {
      String strRecipient = argv[i];
      boolean orcptFlag = strRecipient.toLowerCase(Locale.ENGLISH).trim().contains("orcpt=");
      if (!orcptFlag) {
        logger.debug("jilter envrcpt() {to='" + strRecipient + "'}");
        String recipient =
            strRecipient.toLowerCase(Locale.ENGLISH).trim().replaceAll("<", "").replaceAll(">", "");
        rcpts.add(recipient);

        logger.debug("jilter add recipient {recipient='" + recipient + "'}");
      }
    }
    return JilterStatus.SMFIS_CONTINUE;
  }

  protected boolean shouldIgnoreBCCAddress(String address) {
    MilterServerService milterService = Config.getConfig().getMilterServerService();
    List<String> ignoreAddresses = milterService.getIgnoreBCCAddress();
    Matcher m = headerPattern2.matcher(address.toLowerCase(Locale.ENGLISH).trim());
    if (m.matches()) {
      String mailAddress = m.group(1);
      for (String ignoreAddress : ignoreAddresses) {
        if (ignoreAddress.equalsIgnoreCase(mailAddress)) return true;
      }
    } else {
      m = headerPattern3.matcher(address.toLowerCase(Locale.ENGLISH).trim());
      if (m.matches()) {
        String mailAddress = m.group(1);
        for (String ignoreAddress : ignoreAddresses) {
          if (ignoreAddress.equalsIgnoreCase(mailAddress)) return true;
        }
      }
    }
    return false;
  }

  public JilterStatus eoh() {
    logger.debug("jilter eoh()");
    // includeBCC is false if RCPT TO does not contain at least one field in TO, FROM and CC
    // this is a safety check as sometimes, RCPT TO is something differently entirely
    // and does not contain the actual recipients in the email

    MilterServerService milterService = Config.getConfig().getMilterServerService();

    if (milterService.getIncludeBCC() && includeBCC) {
      logger.debug("including BCC addresses");
      // check to see if address is flagged to ignore
      if (rcpts.size() > 0) {
        Iterator<String> i = rcpts.iterator();
        while (i.hasNext()) {
          String rcpt = i.next();
          if (shouldIgnoreBCCAddress(rcpt)) {
            logger.debug("ignore include bcc address {address='" + rcpt + "'}");
            i.remove();
          }
        }
      }

      if (rcpts.size() > 0) {
        try {
          for (int j = 0; j < rcpts.size(); j++) {
            if (j == 0) {
              bos.write("bcc: ".getBytes());
            } else {
              bos.write(",".getBytes());
            }
            bos.write(rcpts.get(j).getBytes());
          }
          bos.write("\n".getBytes());
        } catch (IOException io) {
          logger.error("jilter failed to write end of header data", io);
        }
      }
    }
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus eom(JilterEOMActions eomActions, Properties properties) {
    logger.debug("jilter eom()");
    try {
      bos.close(); // close stream
    } catch (IOException io) {
      logger.error("jilter failed to close io stream during eom", io);
    }
    byte[] messageBytes = bos.toByteArray();
    bos = new ByteArrayOutputStream();
    ByteArrayInputStream bis = new ByteArrayInputStream(messageBytes);
    try {
      logger.debug("jilter store callback execute");
      Config.getStopBlockFactory()
          .detectBlock("milter server", Thread.currentThread(), this, IDLE_TIMEOUT);
      callback.store(bis, host);
      logger.debug("jilter store callback finished");
    } catch (ArchiveException e) {
      logger.error("failed to store the message via milter", e);
      if (e.getRecoveryDirective() == ArchiveException.RecoveryDirective.REJECT) {
        logger.debug("jilter reject");
        return JilterStatus.SMFIS_REJECT;
      } else if (e.getRecoveryDirective() == ArchiveException.RecoveryDirective.RETRYLATER) {
        logger.debug("jilter temp fail");
        return JilterStatus.SMFIS_TEMPFAIL;
      }
    } catch (Throwable oome) {
      logger.error("failed to store message:" + oome.getMessage(), oome);
      return JilterStatus.SMFIS_REJECT;
    } finally {
      Config.getStopBlockFactory().endDetectBlock(Thread.currentThread());
    }
    return JilterStatus.SMFIS_CONTINUE;
  }

  public int getRequiredModifications() {
    logger.debug("jilter requiredmodifications()");
    return SMFIF_NONE;
  }

  public int getSupportedProcesses() {
    logger.debug("jilter getsupportedprocesses()");
    return PROCESS_CONNECT | PROCESS_ENVRCPT | PROCESS_HEADER | PROCESS_BODY;
  }

  public JilterStatus header(String headerf, String headerv) {

    logger.debug("jilter header {name='" + headerf + "',value='" + headerv + "'}");
    StringBuffer header = new StringBuffer();
    header.append(headerf);
    header.append(": ");
    header.append(headerv);
    header.append("\n");
    try {
      bos.write(header.toString().getBytes());
    } catch (IOException io) {
      logger.error("jilter failed to write header field", io);
    }
    Matcher m = headerPattern1.matcher(headerf.toLowerCase(Locale.ENGLISH).trim());
    if (m.matches()) {
      logger.debug("jilter found to/bcc/cc header");
      String[] addresses = headerv.split(",");
      for (int i = 0; i < addresses.length; i++) {
        includeBCC = includeBCC | rcpts.remove(addresses[i].toLowerCase(Locale.ENGLISH).trim());
        logger.debug("jilter del recipient {recipient='" + addresses[i] + "'}");
        m = headerPattern2.matcher(addresses[i].toLowerCase(Locale.ENGLISH).trim());
        if (m.matches()) {
          String mailAddress = m.group(1);
          includeBCC = includeBCC | rcpts.remove(mailAddress);
          logger.debug("jilter del recipient {recipient='" + mailAddress + "'}");
        } else {
          m = headerPattern3.matcher(addresses[i].toLowerCase(Locale.ENGLISH).trim());
          if (m.matches()) {
            String mailAddress = m.group(1);
            includeBCC = includeBCC | rcpts.remove(mailAddress);
            logger.debug("jilter del recipient {recipient='" + mailAddress + "'}");
          }
        }
      }
    }
    return JilterStatus.SMFIS_CONTINUE;
  }

  public JilterStatus helo(String helohost, Properties properties) {
    logger.debug("jilter helo() " + helohost);
    return JilterStatus.SMFIS_CONTINUE;
  }

  public void handleBlock(Thread thread) {

    try {
      if (socket != null) {
        logger.debug("close socket()");
        socket.close();
      }
    } catch (Exception e) {
      // ignored
    }
    synchronized (this) {
      if (thread != null) {
        logger.debug("interrupt thread()");
        thread.interrupt();
      }
    }
  }
}