org.apache.nifi.processors.email.smtp.SmtpConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.nifi.processors.email.smtp.SmtpConsumer.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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.apache.nifi.processors.email.smtp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.io.IOUtils;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.ProcessSessionFactory;
import org.apache.nifi.processor.exception.FlowFileAccessException;
import org.apache.nifi.processors.email.ListenSMTP;
import org.apache.nifi.stream.io.LimitingInputStream;
import org.apache.nifi.util.StopWatch;
import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.MessageHandler;
import org.subethamail.smtp.RejectException;
import org.subethamail.smtp.TooMuchDataException;
import org.subethamail.smtp.server.SMTPServer;

/**
 * A simple consumer that provides a bridge between 'push' message distribution
 * provided by {@link SMTPServer} and NiFi polling scheduler mechanism.
 */
public class SmtpConsumer implements MessageHandler {

    private String from = null;
    private final List<String> recipientList = new ArrayList<>();
    private final MessageContext context;
    private final ProcessSessionFactory sessionFactory;
    private final int port;
    private final int maxMessageSize;
    private final ComponentLog log;
    private final String host;

    public SmtpConsumer(final MessageContext context, final ProcessSessionFactory sessionFactory, final int port,
            final String host, final ComponentLog log, final int maxMessageSize) {
        this.context = context;
        this.sessionFactory = sessionFactory;
        this.port = port;
        if (host == null || host.trim().isEmpty()) {
            this.host = context.getSMTPServer().getHostName();
        } else {
            this.host = host;
        }
        this.log = log;
        this.maxMessageSize = maxMessageSize;
    }

    String getFrom() {
        return from;
    }

    List<String> getRecipients() {
        return Collections.unmodifiableList(recipientList);
    }

    @Override
    public void data(final InputStream data) throws RejectException, TooMuchDataException, IOException {
        final ProcessSession processSession = sessionFactory.createSession();
        final StopWatch watch = new StopWatch();
        watch.start();
        try {
            FlowFile flowFile = processSession.create();
            final AtomicBoolean limitExceeded = new AtomicBoolean(false);
            flowFile = processSession.write(flowFile, (OutputStream out) -> {
                final LimitingInputStream lis = new LimitingInputStream(data, maxMessageSize);
                IOUtils.copy(lis, out);
                if (lis.hasReachedLimit()) {
                    limitExceeded.set(true);
                }
            });
            if (limitExceeded.get()) {
                throw new TooMuchDataException(
                        "Maximum message size limit reached - client must send smaller messages");
            }
            flowFile = processSession.putAllAttributes(flowFile, extractMessageAttributes());
            watch.stop();
            processSession.getProvenanceReporter().receive(flowFile, "smtp://" + host + ":" + port + "/",
                    watch.getDuration(TimeUnit.MILLISECONDS));
            processSession.transfer(flowFile, ListenSMTP.REL_SUCCESS);
            processSession.commit();
        } catch (FlowFileAccessException | IllegalStateException | RejectException | IOException ex) {
            log.error("Unable to fully process input due to " + ex.getMessage(), ex);
            throw ex;
        } finally {
            processSession.rollback(); //make sure this happens no matter what - is safe
        }
    }

    @Override
    public void from(final String from) throws RejectException {
        this.from = from;
    }

    @Override
    public void recipient(final String recipient) throws RejectException {
        if (recipient != null && recipient.length() < 100 && recipientList.size() < 100) {
            recipientList.add(recipient);
        }
    }

    @Override
    public void done() {
    }

    private Map<String, String> extractMessageAttributes() {
        final Map<String, String> attributes = new HashMap<>();
        final Certificate[] tlsPeerCertificates = context.getTlsPeerCertificates();
        if (tlsPeerCertificates != null) {
            for (int i = 0; i < tlsPeerCertificates.length; i++) {
                if (tlsPeerCertificates[i] instanceof X509Certificate) {
                    X509Certificate x509Cert = (X509Certificate) tlsPeerCertificates[i];
                    attributes.put("smtp.certificate." + i + ".serial", x509Cert.getSerialNumber().toString());
                    attributes.put("smtp.certificate." + i + ".subjectName", x509Cert.getSubjectDN().getName());
                }
            }
        }

        SocketAddress address = context.getRemoteAddress();
        if (address != null) {
            // will extract and format source address if available
            String strAddress = address instanceof InetSocketAddress
                    ? ((InetSocketAddress) address).getHostString() + ":" + ((InetSocketAddress) address).getPort()
                    : context.getRemoteAddress().toString();
            attributes.put("smtp.src", strAddress);
        }

        attributes.put("smtp.helo", context.getHelo());
        attributes.put("smtp.from", from);
        for (int i = 0; i < recipientList.size(); i++) {
            attributes.put("smtp.recipient." + i, recipientList.get(i));
        }
        attributes.put(CoreAttributes.MIME_TYPE.key(), "message/rfc822");
        return attributes;
    }

}