diff --git a/cms-api/src/main/java/com/condation/cms/api/mail/MailService.java b/cms-api/src/main/java/com/condation/cms/api/mail/MailService.java new file mode 100644 index 000000000..e95b316fb --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/mail/MailService.java @@ -0,0 +1,41 @@ +package com.condation.cms.api.mail; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +/** + * + * @author thmar + */ +public interface MailService { + void sendText (String account, Message message); + + void sendHtml (String account, Message message); + + default void sendText (Message message) { + sendText("default", message); + } + + default void sendHtml (Message message) { + sendHtml("default", message); + } +} diff --git a/cms-api/src/main/java/com/condation/cms/api/mail/Message.java b/cms-api/src/main/java/com/condation/cms/api/mail/Message.java new file mode 100644 index 000000000..3ccd44e57 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/mail/Message.java @@ -0,0 +1,38 @@ +package com.condation.cms.api.mail; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +import java.util.List; + +/** + * + * @author thmar + */ +public record Message(String from, List to, String subject, String message) { + + public Message (String from, Recipient to, String subject, String message) { + this(from, List.of(to), subject, message); + } + + public static record Recipient (String name, String mailAddress){} +} diff --git a/cms-core/pom.xml b/cms-core/pom.xml index d27c42eb7..2ded86422 100644 --- a/cms-core/pom.xml +++ b/cms-core/pom.xml @@ -28,6 +28,23 @@ jtoml-serializer-gson 1.3.0 + + org.simplejavamail + simple-java-mail + 8.12.6 + + + com.icegreen + greenmail + 2.1.7 + test + + + com.icegreen + greenmail-junit5 + 2.1.7 + test + org.projectlombok lombok diff --git a/cms-core/src/main/java/com/condation/cms/core/mail/DefaultMailService.java b/cms-core/src/main/java/com/condation/cms/core/mail/DefaultMailService.java new file mode 100644 index 000000000..ad23dc40c --- /dev/null +++ b/cms-core/src/main/java/com/condation/cms/core/mail/DefaultMailService.java @@ -0,0 +1,100 @@ +package com.condation.cms.core.mail; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +import com.condation.cms.api.db.DB; +import com.condation.cms.api.mail.MailService; +import com.condation.cms.api.mail.Message; +import java.nio.file.Files; +import org.simplejavamail.api.email.Recipient; +import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.email.EmailBuilder; +import org.simplejavamail.mailer.MailerBuilder; + +/** + * + * @author thmar + */ +public class DefaultMailService implements MailService { + + private final DB db; + + private MailConfig config; + + public DefaultMailService(DB db) { + this.db = db; + + init(); + } + + private void init () { + var mailConfig = db.getFileSystem().resolve("config/mail.yaml"); + if (Files.exists(mailConfig)) { + config = MailConfigLoader.load(mailConfig); + } + } + + @Override + public void sendText(String account, Message message) { + var acc = getAccount(account); + + var mail = EmailBuilder.startingBlank() + .appendText(message.message()) + .withSubject(message.subject()) + .from(message.from(), acc.getFromMail()) + .to(message.to().stream().map(rec -> new Recipient(rec.name(), rec.mailAddress(), null)).toList()); + + buildMailer(acc).sendMail(mail.buildEmail()); + } + + @Override + public void sendHtml(String account, Message message) { + var acc = getAccount(account); + + var mail = EmailBuilder.startingBlank() + .appendTextHTML(message.message()) + .withSubject(message.subject()) + .from(message.from(), acc.getFromMail()) + .to(message.to().stream().map(rec -> new Recipient(rec.name(), rec.mailAddress(), null)).toList()); + + buildMailer(acc).sendMail(mail.buildEmail()); + } + + private MailConfig.Account getAccount(String account) throws RuntimeException { + var acc = config.getAccount(account); + if (acc.isEmpty()) { + throw new RuntimeException("unknown account"); + } + return acc.get(); + } + + + private Mailer buildMailer (MailConfig.Account account) { + return MailerBuilder.withSMTPServer( + account.getHost(), + account.getPort(), + account.getUsername(), + account.getPassword() + ).buildMailer(); + } +} diff --git a/cms-core/src/main/java/com/condation/cms/core/mail/MailConfig.java b/cms-core/src/main/java/com/condation/cms/core/mail/MailConfig.java new file mode 100644 index 000000000..bb2538150 --- /dev/null +++ b/cms-core/src/main/java/com/condation/cms/core/mail/MailConfig.java @@ -0,0 +1,54 @@ +package com.condation.cms.core.mail; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +import java.util.List; +import java.util.Optional; +import lombok.Data; + +/** + * + * @author thmar + */ +@Data +public class MailConfig { + + private List accounts; + + public Optional getAccount (String name) { + if (accounts == null || accounts.isEmpty()) { + return Optional.empty(); + } + return accounts.stream().filter(acc -> name.equals(acc.getName())).findFirst(); + } + + @Data + public static class Account { + private String name; + private String fromMail; + private String host; + private int port; + private String username; + private String password; + } +} diff --git a/cms-core/src/main/java/com/condation/cms/core/mail/MailConfigLoader.java b/cms-core/src/main/java/com/condation/cms/core/mail/MailConfigLoader.java new file mode 100644 index 000000000..263e40128 --- /dev/null +++ b/cms-core/src/main/java/com/condation/cms/core/mail/MailConfigLoader.java @@ -0,0 +1,48 @@ +package com.condation.cms.core.mail; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +/** + * + * @author thorstenmarx + */ +public class MailConfigLoader { + + public static MailConfig load(Path configFile) { + try { + try (var configByteBuffer = Files.newBufferedReader(configFile, StandardCharsets.UTF_8)) { + Yaml yaml = new Yaml(new Constructor(MailConfig.class, new LoaderOptions())); + return yaml.load(configByteBuffer); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/cms-core/src/test/java/com/condation/cms/core/mail/DefaultMailServiceTest.java b/cms-core/src/test/java/com/condation/cms/core/mail/DefaultMailServiceTest.java new file mode 100644 index 000000000..13983c9ae --- /dev/null +++ b/cms-core/src/test/java/com/condation/cms/core/mail/DefaultMailServiceTest.java @@ -0,0 +1,92 @@ +package com.condation.cms.core.mail; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +import com.condation.cms.api.db.DB; +import com.condation.cms.api.db.DBFileSystem; +import com.condation.cms.api.mail.Message; +import com.icegreen.greenmail.junit5.GreenMailExtension; +import com.icegreen.greenmail.util.ServerSetupTest; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; + +/** + * + * @author thorstenmarx + */ +public class DefaultMailServiceTest { + + @TempDir + Path tempDir; + + @RegisterExtension + static GreenMailExtension greenMail = new GreenMailExtension(ServerSetupTest.SMTP); + + + + @Test + public void test_mailer() throws IOException { + + greenMail.setUser("user1", "pass1"); + + String yaml = """ + accounts: + - name: default + host: localhost + fromMail: from@example.com + port: %s + username: user1 + password: pass1 + """.formatted(greenMail.getSmtp().getPort()); + Path configFile = createTempFile(yaml); + + var db = Mockito.mock(DB.class); + var fileSystem = Mockito.mock(DBFileSystem.class); + + Mockito.when(db.getFileSystem()).thenReturn(fileSystem); + Mockito.when(fileSystem.resolve("config/mail.yaml")).thenReturn(configFile); + + var mailService = new DefaultMailService(db); + + var message = new Message("noreply", new com.condation.cms.api.mail.Message.Recipient("to", "to@example.com"), "Hello", "Hello World!"); + + mailService.sendText(message); + + Assertions.assertThat(greenMail.getReceivedMessagesForDomain("to@example.com")).hasSize(1); + } + + + // Helper Methode + private Path createTempFile(String yamlContent) throws IOException { + Path file = tempDir.resolve("mail.yaml"); + Files.write(file, yamlContent.getBytes(StandardCharsets.UTF_8)); + return file; + } +} diff --git a/cms-core/src/test/java/com/condation/cms/core/mail/MailConfigLoaderTest.java b/cms-core/src/test/java/com/condation/cms/core/mail/MailConfigLoaderTest.java new file mode 100644 index 000000000..3ddca6890 --- /dev/null +++ b/cms-core/src/test/java/com/condation/cms/core/mail/MailConfigLoaderTest.java @@ -0,0 +1,186 @@ +package com.condation.cms.core.mail; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * . + * #L% + */ + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import static org.assertj.core.api.Assertions.*; + +@DisplayName("MailConfigLoader Tests") +class MailConfigLoaderTest { + + @TempDir + Path tempDir; + + @Test + @DisplayName("sollte valide YAML mit Accounts auf MailConfig mappen") + void testLoadValidConfig() throws IOException { + // Arrange + String yaml = """ + accounts: + - host: smtp.example.com + port: 587 + username: test@example.com + password: mypassword + - host: smtp.gmail.com + port: 465 + username: user@gmail.com + password: gmailpass + """; + Path configFile = createTempYamlFile(yaml); + + // Act + MailConfig config = MailConfigLoader.load(configFile); + + // Assert + assertThat(config).isNotNull(); + assertThat(config.getAccounts()) + .hasSize(2) + .extracting("host", "port", "username") + .containsExactly( + tuple("smtp.example.com", 587, "test@example.com"), + tuple("smtp.gmail.com", 465, "user@gmail.com") + ); + } + + @Test + @DisplayName("sollte einzelnen Account korrekt mappen") + void testLoadSingleAccount() throws IOException { + // Arrange + String yaml = """ + accounts: + - host: mail.server.de + port: 587 + username: admin + password: secret123 + """; + Path configFile = createTempYamlFile(yaml); + + // Act + MailConfig config = MailConfigLoader.load(configFile); + + // Assert + assertThat(config.getAccounts()) + .hasSize(1) + .first() + .hasFieldOrPropertyWithValue("host", "mail.server.de") + .hasFieldOrPropertyWithValue("port", 587) + .hasFieldOrPropertyWithValue("username", "admin") + .hasFieldOrPropertyWithValue("password", "secret123"); + } + + @Test + @DisplayName("sollte leere Accounts-Liste handhaben") + void testLoadEmptyAccounts() throws IOException { + // Arrange + String yaml = """ + accounts: [] + """; + Path configFile = createTempYamlFile(yaml); + + // Act + MailConfig config = MailConfigLoader.load(configFile); + + // Assert + assertThat(config.getAccounts()).isEmpty(); + } + + @Test + @DisplayName("sollte MailConfig-Instanz zurückgeben") + void testLoadReturnsMailConfigInstance() throws IOException { + // Arrange + String yaml = """ + accounts: + - host: localhost + port: 25 + username: user + password: pass + """; + Path configFile = createTempYamlFile(yaml); + + // Act + MailConfig config = MailConfigLoader.load(configFile); + + // Assert + assertThat(config).isInstanceOf(MailConfig.class); + } + + @Test + @DisplayName("sollte IOException werfen wenn Datei nicht existiert") + void testLoadNonExistentFile() { + // Arrange + Path nonExistentFile = Paths.get(tempDir.toString(), "not-exists.yaml"); + + // Act & Assert + assertThatThrownBy(() -> MailConfigLoader.load(nonExistentFile)) + .isInstanceOf(RuntimeException.class); + } + + @Test + @DisplayName("sollte mehrere Accounts mit unterschiedlichen Properties mappen") + void testLoadMultipleAccountsWithDifferentValues() throws IOException { + // Arrange + String yaml = """ + accounts: + - host: smtp1.example.com + port: 587 + username: user1@example.com + password: pass1 + - host: smtp2.example.com + port: 465 + username: user2@example.com + password: pass2 + - host: smtp3.example.com + port: 25 + username: user3@example.com + password: pass3 + """; + Path configFile = createTempYamlFile(yaml); + + // Act + MailConfig config = MailConfigLoader.load(configFile); + + // Assert + assertThat(config.getAccounts()) + .hasSize(3) + .extracting("host", "port") + .containsExactly( + tuple("smtp1.example.com", 587), + tuple("smtp2.example.com", 465), + tuple("smtp3.example.com", 25) + ); + } + + // Helper Methode + private Path createTempYamlFile(String yamlContent) throws IOException { + Path file = tempDir.resolve("config.yaml"); + Files.write(file, yamlContent.getBytes(StandardCharsets.UTF_8)); + return file; + } +} diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java index 142203f4f..74a58cbb6 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java @@ -44,6 +44,7 @@ import com.condation.cms.api.db.cms.ReadOnlyFile; import com.condation.cms.api.eventbus.EventBus; import com.condation.cms.api.eventbus.events.ConfigurationReloadEvent; +import com.condation.cms.api.mail.MailService; import com.condation.cms.api.mapper.ContentNodeMapper; import com.condation.cms.api.media.MediaService; import com.condation.cms.api.messages.MessageSource; @@ -65,6 +66,7 @@ import com.condation.cms.core.configuration.ConfigurationFactory; import com.condation.cms.core.configuration.properties.ExtendedSiteProperties; import com.condation.cms.core.eventbus.MessagingEventBus; +import com.condation.cms.core.mail.DefaultMailService; import com.condation.cms.core.messages.DefaultMessageSource; import com.condation.cms.core.messaging.DefaultMessaging; import com.condation.cms.core.scheduler.SiteCronJobScheduler; @@ -311,4 +313,11 @@ public CronJobContext cronJobContext() { return cronJobContext; } + + + @Provides + @Singleton + public MailService mailServife (DB db) { + return new DefaultMailService(db); + } } diff --git a/modules/ui-module/pom.xml b/modules/ui-module/pom.xml index cad06444d..f8e85b245 100644 --- a/modules/ui-module/pom.xml +++ b/modules/ui-module/pom.xml @@ -35,11 +35,6 @@ com.condation.cms cms-templates - - org.simplejavamail - simple-java-mail - 8.12.6 - org.projectlombok lombok diff --git a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/extensionpoints/UILifecycleExtension.java b/modules/ui-module/src/main/java/com/condation/cms/modules/ui/extensionpoints/UILifecycleExtension.java index ac1e91709..13c3a2b3f 100644 --- a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/extensionpoints/UILifecycleExtension.java +++ b/modules/ui-module/src/main/java/com/condation/cms/modules/ui/extensionpoints/UILifecycleExtension.java @@ -22,15 +22,10 @@ * #L% */ import com.condation.cms.api.feature.features.CacheManagerFeature; -import com.condation.cms.api.feature.features.DBFeature; import com.condation.cms.api.module.SiteModuleContext; import com.condation.cms.modules.ui.services.LockService; import com.condation.cms.modules.ui.utils.TemplateEngine; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import lombok.Getter; -import org.simplejavamail.config.ConfigLoader; /** * diff --git a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/http/auth/AjaxLoginHandler.java b/modules/ui-module/src/main/java/com/condation/cms/modules/ui/http/auth/AjaxLoginHandler.java index 55c134d94..fa9bdf63b 100644 --- a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/http/auth/AjaxLoginHandler.java +++ b/modules/ui-module/src/main/java/com/condation/cms/modules/ui/http/auth/AjaxLoginHandler.java @@ -27,6 +27,8 @@ import com.condation.cms.api.feature.features.ConfigurationFeature; import com.condation.cms.api.feature.features.InjectorFeature; import com.condation.cms.api.feature.features.IsDevModeFeature; +import com.condation.cms.api.mail.MailService; +import com.condation.cms.api.mail.Message; import com.condation.cms.api.module.SiteModuleContext; import com.condation.cms.api.request.RequestContext; import com.condation.cms.api.utils.RequestUtil; @@ -34,7 +36,6 @@ import com.condation.cms.auth.services.User; import com.condation.cms.auth.services.UserService; import com.condation.cms.modules.ui.http.JettyHandler; -import com.condation.cms.modules.ui.utils.MailerProvider; import com.condation.cms.modules.ui.utils.TokenUtils; import com.condation.cms.modules.ui.utils.json.UIGsonProvider; import java.security.SecureRandom; @@ -49,7 +50,6 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.Callback; -import org.simplejavamail.email.EmailBuilder; /** * @@ -230,19 +230,20 @@ private void sendLoginCode(User user, String code) { var siteProperties = moduleContext.get(ConfigurationFeature.class).configuration().get(SiteConfiguration.class).siteProperties(); - var mailer = MailerProvider.provide(siteProperties.id()); - var mail = EmailBuilder.startingBlank() - .to(user.username(), (String) user.data().getOrDefault("mail", "test@localhost.de")) - .from( - (String)siteProperties.getOrDefault("ui.2fa.mail_sender", "CondationCMS"), - (String)siteProperties.get("ui.2fa.mail_from") - ).withSubject(siteProperties.getOrDefault("ui.2fa.mail_title", "CondationCMS login code")) - .withPlainText( - siteProperties.getOrDefault("ui.2fa.mail_message", "your code: ") + var mailService = moduleContext.get(InjectorFeature.class).injector().getInstance(MailService.class); + + var message = new Message( + (String)siteProperties.getOrDefault("ui.2fa.mail_sender", "CondationCMS"), + new com.condation.cms.api.mail.Message.Recipient( + user.username(), + (String) user.data().getOrDefault("mail", "test@localhost.de")), + siteProperties.getOrDefault("ui.2fa.mail_title", "CondationCMS login code"), + siteProperties.getOrDefault("ui.2fa.mail_message", "your code: ") .replace("", code) .replace("", user.username()) - ).buildEmail(); - mailer.sendMail(mail); + ); + + mailService.sendText(message); } private int getClientLoginAttempts(Request request) { diff --git a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/utils/MailerProvider.java b/modules/ui-module/src/main/java/com/condation/cms/modules/ui/utils/MailerProvider.java deleted file mode 100644 index 3475594eb..000000000 --- a/modules/ui-module/src/main/java/com/condation/cms/modules/ui/utils/MailerProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.condation.cms.modules.ui.utils; - -/*- - * #%L - * ui-module - * %% - * Copyright (C) 2023 - 2025 CondationCMS - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * . - * #L% - */ - -import org.simplejavamail.api.mailer.Mailer; -import org.simplejavamail.mailer.MailerBuilder; -import org.simplejavamail.api.mailer.config.TransportStrategy; - -public class MailerProvider { - - public static Mailer provide(String siteId) { - var key = normalizeHostId(siteId); - String host = getRequiredEnv("CMS_UI_SMTP_HOST_%s".formatted(key)); - int port = Integer.parseInt(getRequiredEnv("CMS_UI_SMTP_PORT_%s".formatted(key))); - String username = getRequiredEnv("CMS_UI_SMTP_USER_%s".formatted(key)); - String password = getRequiredEnv("CMS_UI_SMTP_PASS_%s".formatted(key)); - - String transport = System.getenv().getOrDefault("CMS_UI_SMTP_TRANSPORT_%s".formatted(key), "SMTP_TLS"); - - TransportStrategy strategy = switch (transport.toUpperCase()) { - case "SMTP_TLS" -> - TransportStrategy.SMTP_TLS; - case "SMTPS" -> - TransportStrategy.SMTPS; - case "SMTP_PLAIN" -> - TransportStrategy.SMTP; - default -> - throw new IllegalArgumentException("Unsupported SMTP_TRANSPORT: " + transport); - }; - - return MailerBuilder - .withSMTPServer(host, port, username, password) - .withTransportStrategy(strategy) - .buildMailer(); - } - - private static String getRequiredEnv(String name) { - String value = System.getenv(name); - if (value == null || value.isEmpty()) { - throw new IllegalStateException("Missing required environment variable: " + name); - } - return value; - } - - private static String normalizeHostId(String hostId) { - return hostId.toUpperCase().replaceAll("[^A-Z0-9]", "_"); - } -}