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

org.nightcode.milter.AbstractMilterHandler 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;

import java.net.SocketAddress;
import java.util.List;
import java.util.Map;

import org.jetbrains.annotations.Nullable;
import org.nightcode.milter.codec.MilterPacket;
import org.nightcode.milter.net.MilterPacketSender;
import org.nightcode.milter.util.ByteArrays;
import org.nightcode.milter.util.Log;
import org.nightcode.milter.util.MilterPackets;

import static java.lang.String.format;
import static org.nightcode.milter.CommandCode.SMFIC_OPTNEG;

public abstract class AbstractMilterHandler implements MilterHandler {

  private final Actions milterActions;
  private final ProtocolSteps milterProtocolSteps;

  protected final MessageModificationService messageModificationService;

  protected AbstractMilterHandler(Actions milterActions, ProtocolSteps milterProtocolSteps) {
    this(milterActions, milterProtocolSteps, new MessageModificationServiceImpl());
  }

  protected AbstractMilterHandler(Actions milterActions, ProtocolSteps milterProtocolSteps,
                                  MessageModificationService messageModificationService) {
    this.milterActions              = milterActions;
    this.milterProtocolSteps        = milterProtocolSteps;
    this.messageModificationService = messageModificationService;
  }

  @Override public void abort(MilterContext context, @Nullable MilterPacket packet) throws MilterException {
    // do nothing
  }

  @Override public void body(MilterContext context, byte[] bodyChunk) throws MilterException {
    context.sendContinue();
  }

  @Override public void connect(MilterContext context, String hostname, int family, int port, @Nullable SocketAddress address)
      throws MilterException {
    context.sendContinue();
  }

  @Override public void macro(MilterContext context, int type, Map macros) {
    context.setMacros(type, macros);
  }

  @Override public void eob(MilterContext context, @Nullable byte[] bodyChunk) throws MilterException {
    context.sendContinue();
  }

  @Override public void helo(MilterContext context, String helohost) throws MilterException {
    context.sendContinue();
  }

  @Override public void quitNc(MilterContext context) {
    // do nothing
  }

  @Override public void header(MilterContext context, String headerName, String headerValue) throws MilterException {
    context.sendContinue();
  }

  @Override public void envfrom(MilterContext context, List from) throws MilterException {
    context.sendContinue();
  }

  @Override public void eoh(MilterContext context) throws MilterException {
    context.sendContinue();
  }

  @Override public void optneg(MilterContext context, int mtaProtocolVersion, Actions mtaActions, ProtocolSteps mtaProtocolSteps)
      throws MilterException {
    Log.debug().log(getClass(), format("protocol negotiation\n"
            + "\tMTA    { Version: %s %s %s}\n"
            + "\tMilter { Version: %s %s %s}"
        , mtaProtocolVersion
        , mtaActions
        , mtaProtocolSteps
        , context.milterProtocolVersion()
        , context.milterActions()
        , context.milterProtocolSteps()
    ));

    context.setMtaProtocolVersion(mtaProtocolVersion);

    int sessionProtocolVersion = Math.min(context.milterProtocolVersion(), mtaProtocolVersion);
    context.setSessionProtocolVersion(sessionProtocolVersion);

    if (mtaActions.bitmap() == 0) {
      mtaActions = Actions.DEF_ACTIONS;
    }
    context.setMtaActions(mtaActions);

    if ((context.getMtaActions().bitmap() & context.milterActions().bitmap()) != context.milterActions().bitmap()) {
      Log.warn().log(getClass(), format("MTA %s doesn't fulfill Milter requirements %s", context.getMtaActions(), context.milterActions()));
      abortSession(context, null);
      return;
    }

    if (mtaProtocolSteps.bitmap() == 0) {
      mtaProtocolSteps = ProtocolSteps.DEF_PROTOCOL_STEPS;
    }
    context.setMtaProtocolSteps(mtaProtocolSteps);

    byte[] buffer = new byte[4];
    for (int i = 0; i < buffer.length; i++) {
      buffer[i] = (byte) (mtaProtocolSteps.array()[i] & context.milterProtocolSteps().array()[i]);
    }
    context.setSessionProtocolSteps(new ProtocolSteps(buffer, 0));

    byte[] version       = ByteArrays.intToByteArray(context.getSessionProtocolVersion());
    byte[] actions       = context.milterActions().array();
    byte[] protocolSteps = context.getSessionProtocolSteps().array();

    byte[] payload = new byte[version.length + actions.length + protocolSteps.length];

    System.arraycopy(version, 0, payload, 0, version.length);
    System.arraycopy(actions, 0, payload, version.length, actions.length);
    System.arraycopy(protocolSteps, 0, payload, version.length + actions.length, protocolSteps.length);

    MilterPacket response = MilterPacket.builder()
        .command(SMFIC_OPTNEG)
        .payload(payload)
        .build();
    context.sendPacket(response);
  }

  @Override public void envrcpt(MilterContext context, List recipients) throws MilterException {
    context.sendContinue();
  }

  @Override public void data(MilterContext context, byte[] payload) throws MilterException {
    context.sendContinue();
  }

  @Override public void unknown(MilterContext context, byte[] payload) throws MilterException {
    context.sendContinue();
  }

  @Override public void abortSession(MilterContext context, MilterPacket packet) {
    if (MilterPackets.isMessageState(context.getSessionStep())) {
      try {
        abort(context, packet);
      } catch (MilterException ex) {
        Log.info().log(getClass(), () -> format("[%s] can't execute abort command", context.id()), ex);
      }
    }
    closeSession(context);
  }

  @Override public void closeSession(MilterContext context) {
    try {
      quit(context);
    } finally {
      context.destroy();
    }
  }

  @Override public MilterContext createContext(MilterPacketSender sender) {
    return new MilterContextImpl(this, milterActions, milterProtocolSteps, sender);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy