JakartaMailSender.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2026 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.openspcoop2.utils.mail;

import jakarta.activation.DataHandler;
import jakarta.activation.FileDataSource;
import jakarta.mail.*;
import jakarta.mail.internet.*;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;
import java.util.UUID;

import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.openspcoop2.utils.transport.http.SSLUtilities;
import org.slf4j.Logger;

/**
 * CommonsNetSender
 *
 * @author Poli Andrea (apoli@link.it)
 * @author Pintori Giuliano (giuliano.pintori@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class JakartaMailSender extends Sender {

    protected JakartaMailSender(Logger log) {
        super(log);
    }

    @Override
    public void send(Mail mail, boolean debug) throws UtilsException {
        List<File> tempFiles = new ArrayList<>();
        try {
            // Proprietà di connessione
            Properties props = new Properties();
            props.put("mail.smtp.host", mail.getServerHost());
            props.put("mail.smtp.port", String.valueOf(mail.getServerPort()));
            props.put("mail.smtp.connectiontimeout", String.valueOf(this.getConnectionTimeout()));
            props.put("mail.smtp.timeout", String.valueOf(this.getReadTimeout()));

            if (mail.isStartTls()) {
                props.put("mail.smtp.starttls.enable", "true");
            }
            if (mail.getSslConfig() != null) {
                // Forza SSL diretto (SMTPS)
                props.put("mail.smtp.ssl.enable", "true");
                // Configurazioni aggiuntive come hostname verifier
                props.put("mail.smtp.ssl.checkserveridentity", String.valueOf(mail.getSslConfig().isHostnameVerifier()));
                StringBuilder bf = new StringBuilder();
                SSLUtilities.setSSLContextIntoJavaProperties(mail.getSslConfig(), bf);
                if (debug) this.logDebug(bf.toString());
            }

            Session session = initSession(mail, props, debug);

            // Costruzione messaggio
            MimeMessage message = buildBaseMessage(mail, session, debug);
           
            boolean hasAttachments = mail.getBody().getAttachments() != null &&
                    !mail.getBody().getAttachments().isEmpty();

            if (hasAttachments) {
            	addAttachments(mail, tempFiles, message, debug);
            } else {
                // Solo testo
                message.setText(mail.getBody().getMessage(), "UTF-8");
            }

            // Invio
            this.logDebug("Send ["+mail.getServerHost()+":"+mail.getServerPort()+"] ...");
            Transport.send(message);
            this.logDebug("Send ["+mail.getServerHost()+":"+mail.getServerPort()+"] completed");
            
        } catch (Exception e) {
            throw new UtilsException(e.getMessage(), e);
        } finally {
            // Pulizia temporanei
            for (File f : tempFiles) {
            	FileSystemUtilities.deleteFile(f);
            }
        }
    }
    
    private Session initSession(Mail mail, Properties props, boolean debug) {
    	this.logDebug("Init session username["+mail.getUsername()+"] ...");
    	Session session;
        if (mail.getUsername() != null) {
        	props.put("mail.smtp.auth", "true");
        	
            session = Session.getInstance(props, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(mail.getUsername(), mail.getPassword());
                }
            });
        } else {
            session = Session.getInstance(props);
        }
        session.setDebug(debug);
        this.logDebug("Init session username["+mail.getUsername()+"] completed");
        return session;
    }
    
    private MimeMessage buildBaseMessage(Mail mail, Session session, boolean debug) throws MessagingException {
    	if(debug){
    		this.logDebug("Build message ...");
    	}
    	MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(mail.getFrom()));
        message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.getTo()));

        if (mail.getCc() != null && !mail.getCc().isEmpty()) {
            message.setRecipients(Message.RecipientType.CC,
                    InternetAddress.parse(String.join(",", mail.getCc())));
        }

        message.setSubject(mail.getSubject());

        // Headers extra
        if (mail.getUserAgent() != null) {
            message.setHeader("User-Agent", mail.getUserAgent());
        }
        if (mail.getMessageIdDomain() != null) {
            message.setHeader("Message-ID", "<" + UUID.randomUUID() + "@" + mail.getMessageIdDomain() + ">");
        }
        if (mail.getContentLanguage() != null) {
            message.setHeader("Content-Language", mail.getContentLanguage());
        }
        if(debug){
    		this.logDebug("Build message completed");
        }
        return message;
    }
    
    private void addAttachments(Mail mail, List<File> tempFiles, MimeMessage message, boolean debug) throws MessagingException, IOException, UtilsException {
    	if(debug){
    		this.logDebug("Add attachments ...");
    	}
    	 // Corpo multiparte
        MimeMultipart multipart = new MimeMultipart();

        // Parte testo
        if (mail.getBody().getMessage() != null) {
            MimeBodyPart textPart = new MimeBodyPart();
            textPart.setText(mail.getBody().getMessage(), "UTF-8");
            multipart.addBodyPart(textPart);
        }

        // Allegati
        for (MailAttach attach : mail.getBody().getAttachments()) {
            File tmpFile;
            switch (attach) {
              case MailTextAttach mailTextAttach -> {
                  tmpFile = FileSystemUtilities.createTempFile("mailTextAttach", ".txt");
                  FileSystemUtilities.writeFile(tmpFile,
                  		mailTextAttach.getContent().getBytes());
              }
              case MailBinaryAttach mailBinaryAttach -> {
                  tmpFile = FileSystemUtilities.createTempFile("mailBinaryAttach", ".bin");
                  FileSystemUtilities.writeFile(tmpFile,
                  		mailBinaryAttach.getContent());
              }
              default -> 
              	throw new UtilsException("MailAttach class '"+attach.getClass().getName()+"' unsupported");
            }
            tempFiles.add(tmpFile);

            MimeBodyPart attachmentPart = new MimeBodyPart();
            attachmentPart.setDataHandler(new DataHandler(new FileDataSource(tmpFile)));
            attachmentPart.setFileName(attach.getName());
            multipart.addBodyPart(attachmentPart);
        }

        message.setContent(multipart);
        if(debug){
    		this.logDebug("Add attachments completed ...");
        }
    }
}