All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nightcode.milter.util.MilterPacketUtil Maven / Gradle / Ivy

There is a newer version: 0.8
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.nightcode.milter.util;

import org.nightcode.milter.MilterState;
import org.nightcode.milter.net.MilterPacket;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import static org.nightcode.milter.MessageModificationService.SMFIR_ACCEPT;
import static org.nightcode.milter.MessageModificationService.SMFIR_CONTINUE;
import static org.nightcode.milter.MessageModificationService.SMFIR_DISCARD;
import static org.nightcode.milter.MessageModificationService.SMFIR_REJECT;
import static org.nightcode.milter.MessageModificationService.SMFIR_SKIP;
import static org.nightcode.milter.MessageModificationService.SMFIR_TEMPFAIL;

public final class MilterPacketUtil {

  /**
   * Continue processing the current connection, message, or recipient.
   */
  public static final MilterPacket SMFIS_CONTINUE = MilterPacket.builder().command((byte) SMFIR_CONTINUE).build();

  /**
   * For a connection-oriented routine, reject this connection; call close().
   * For a message-oriented routine (except abort()), reject this message.
   * For a recipient-oriented routine, reject the current recipient (but continue processing the current message).
   */
  public static final MilterPacket SMFIS_REJECT = MilterPacket.builder().command((byte) SMFIR_REJECT).build();

  /**
   * For a message- or recipient-oriented routine, accept this message, but silently discard it.
   * Should not be returned by a connection-oriented routine.
   */
  public static final MilterPacket SMFIS_DISCARD = MilterPacket.builder().command((byte) SMFIR_DISCARD).build();

  /**
   * For a connection-oriented routine, accept this connection without further filter processing; call close().
   * For a message- or recipient-oriented routine, accept this message without further filtering.
   */
  public static final MilterPacket SMFIS_ACCEPT = MilterPacket.builder().command((byte) SMFIR_ACCEPT).build();

  /**
   * Return a temporary failure, i.e., the corresponding SMTP command will return an appropriate 4xx status code.
   * For a message-oriented routine (except envfrom), fail for this message.
   * For a connection-oriented routine, fail for this connection; call close().
   * For a recipient-oriented routine, only fail for the current recipient; continue message processing.
   */
  public static final MilterPacket SMFIS_TEMPFAIL = MilterPacket.builder().command((byte) SMFIR_TEMPFAIL).build();

  /**
   * Skip further callbacks of the same type in this transaction. Currently this return value
   * is only allowed in body(). It can be used if a milter has received sufficiently many
   * body chunks to make a decision, but still wants to invoke message modification functions
   * that are only allowed to be called from eom(). Note: the milter must negotiate this
   * behavior with the MTA, i.e., it must check whether the protocol action SMFIP_SKIP is available
   * and if so, the milter must request it.
   */
  public static final MilterPacket SMFIS_SKIP = MilterPacket.builder().command((byte) SMFIR_SKIP).build();

  private static final byte ZERO_TERM = (byte) 0;

  private static final EnumSet MESSAGE_STATES = EnumSet.of(
        MilterState.MAIL_FROM
      , MilterState.RECIPIENTS
      , MilterState.DATA
      , MilterState.HEADERS
      , MilterState.EOH
      , MilterState.BODY
  );

  /**
   * Return the index of the first occurrence of the \0 in
   * the supplied buffer, or -1 if this buffer does not contain \0.
   *
   * @param buffer a buffer to be searched
   * @return the index of the first occurrence of the \0 in
   *         the supplied buffer, or -1 if this buffer does not contain \0
   */
  public static int indexOfZeroTerm(byte[] buffer) {
    return indexOfZeroTerm(buffer, 0);
  }

  /**
   * Returns the index of the first occurrence of the \0 starting from the offset
   * in the supplied buffer, or -1 if this buffer does not contain \0.
   *
   * @param buffer a buffer to be searched
   * @param offset starting position in the supplied buffer
   * @return the index of the first occurrence of the \0 starting from the offset
   *         in the supplied buffer, or -1 if this buffer does not contain \0
   */
  public static int indexOfZeroTerm(byte[] buffer, int offset) {
    if (offset < 0 || offset > buffer.length) {
      throw new IllegalArgumentException("illegal 'offset' value: " + offset
          + ", supplied buffer size: " + buffer.length);
    }

    for (int i = offset; i < buffer.length; i++) {
      if (buffer[i] == ZERO_TERM) {
        return i;
      }
    }
    return -1;
  }

  /**
   * Indicates if the last byte in a buffer is \0.
   *
   * @param buffer a buffet to be checked
   * @return true if the last bytes in a buffer is \0, else return false
   */
  public static boolean isLastZeroTerm(byte[] buffer) {
    return buffer[buffer.length - 1] == ZERO_TERM;
  }

  public static boolean isMessageState(MilterState state) {
    return MESSAGE_STATES.contains(state);
  }

  /**
   * Splits the supplied buffer to strings, separated by \0.
   *
   * @param buffer the supplied buffer to be splitted
   * @return the list of strings
   */
  public static List splitByZeroTerm(byte[] buffer) {
    return splitByZeroTerm(buffer, 0);
  }

  public static List splitByZeroTerm(byte[] buffer, int offset) {
    if (offset < 0 || offset > buffer.length) {
      throw new IllegalArgumentException("illegal 'offset' value: " + offset
          + ", supplied buffer size: " + buffer.length);
    }
    List result = new ArrayList<>();
    int i;
    while ((i = indexOfZeroTerm(buffer, offset)) > 0) {
      String str = new String(buffer, offset, i - offset, StandardCharsets.UTF_8);
      result.add(str);
      offset = ++i;
    }
    return result;
  }

  private MilterPacketUtil() {
    throw new AssertionError();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy