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

org.mailster.smtp.core.SMTPDecoderContext Maven / Gradle / Ivy

package org.mailster.smtp.core;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.CharsetDecoder;

import javax.mail.util.SharedByteArrayInputStream;

import org.apache.mina.core.buffer.IoBuffer;
import org.mailster.smtp.util.SharedTmpFileInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The SMTP protocol decoder context is used when a client
 * command is split among multiple network packets.
 *
 * @author De Oliveira Edouard <[email protected]>
 */
public class SMTPDecoderContext {

    private static final Logger LOG = LoggerFactory.getLogger(SMTPDecoderContext.class);

    private final CharsetDecoder charsetDecoder;
    private IoBuffer buf;
    private int matchCount = 0;
    private int overflowPosition = 0;
    private boolean thresholdReached = false;
    private boolean dataMode = false;

    /**
     * If we switch to file output, this is the file.
     */
    private File outFile;

    /**
     * If we switch to file output, this is the stream to write to the file.
     */
    private FileOutputStream stream;

    private SMTPDecoder decoder;

    protected SMTPDecoderContext(SMTPDecoder decoder) {
        this.decoder = decoder;
        charsetDecoder = decoder.getCharset().newDecoder();
        buf = IoBuffer.allocate(decoder.getMaxLineLength()).setAutoExpand(true);
    }

    private static byte[] asArray(IoBuffer b) {
        var len = b.remaining();
        var array = new byte[len];
        b.get(array, 0, len);

        return array;
    }

    protected CharsetDecoder getDecoder() {
        return charsetDecoder;
    }

    protected IoBuffer getBuffer() {
        return buf;
    }

    private void compactBuffer() {
        if (dataMode && buf.capacity() > decoder.getMaxLineLength()) {
            buf = IoBuffer.allocate(decoder.getMaxLineLength()).setAutoExpand(true);
        } else {
            buf.clear();
        }
    }

    protected int getOverflowPosition() {
        return overflowPosition;
    }

    protected int getMatchCount() {
        return matchCount;
    }

    protected void setMatchCount(int matchCount) {
        this.matchCount = matchCount;
    }

    protected void reset() throws IOException {
        overflowPosition = 0;
        matchCount = 0;
        charsetDecoder.reset();
        if (thresholdReached) {
            thresholdReached = false;
            compactBuffer();
            closeOutputStream();
        }
    }

    protected void write(IoBuffer b) throws IOException {
        if (dataMode) {
            write(asArray(b));
        } else {
            append(b);
        }
    }

    private void write(byte[] src) throws IOException {
        var predicted = this.thresholdReached ? 0 : this.buf.position() + src.length;

        // Checks whether reading count bytes would cross the limit.
        if (this.thresholdReached || predicted > decoder.getThreshold()) {
            // If previously hit, then use the stream.
            if (!this.thresholdReached) {
                thresholdReached();
            }

            this.stream.write(src);
        } else {
            this.buf.put(src);
        }
    }

    /**
     * Called when the threshold is about to be exceeded. Once called, it
     * won't be called again for the current data transfer.
     */
    private void thresholdReached() throws IOException {
        this.outFile = File.createTempFile(SMTPDecoder.TMPFILE_PREFIX, SMTPDecoder.TMPFILE_SUFFIX);

        LOG.debug("Writing message to file : {}", outFile.getAbsolutePath());

        this.stream = new FileOutputStream(this.outFile);
        this.buf.flip();
        this.stream.write(asArray(this.buf));
        this.thresholdReached = true;
        this.buf.clear();
        LOG.debug("ByteBuffer written to stream");
    }

    protected void closeOutputStream() throws IOException {
        if (this.stream != null) {
            this.stream.flush();
            this.stream.close();
            LOG.debug("Temp file writing achieved - closing stream");
        }
    }

    protected InputStream getNewInputStream() throws IOException {
        if (this.thresholdReached) {
            return new SharedTmpFileInputStream(this.outFile);
        } else {
            return new SharedByteArrayInputStream(asArray(this.buf));
        }
    }

    private void append(IoBuffer in) {
        if (overflowPosition != 0) {
            discard(in);
        } else {
            var pos = buf.position();
            if ((pos + in.remaining()) > decoder.getMaxLineLength()) {
                overflowPosition = pos;
                buf.clear();
                discard(in);
            } else {
                this.buf.put(in);
            }
        }
    }

    private void discard(IoBuffer in) {
        if (Integer.MAX_VALUE < (overflowPosition + in.remaining())) {
            overflowPosition = Integer.MAX_VALUE;
        } else {
            overflowPosition += in.remaining();
        }

        in.position(in.limit());
    }

    public void setDataMode(boolean dataMode) {
        this.dataMode = dataMode;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy