/bundles/org.openhab.binding.luxom/ @jesperskriasoft
/bundles/org.openhab.binding.luxtronikheatpump/ @sgiehl
/bundles/org.openhab.binding.magentatv/ @markus7017
-/bundles/org.openhab.binding.mail/ @openhab/add-ons-maintainers
+/bundles/org.openhab.binding.mail/ @J-N-K
/bundles/org.openhab.binding.max/ @marcelrv
/bundles/org.openhab.binding.mcd/ @simon-dengler
/bundles/org.openhab.binding.mcp23017/ @aogorek
## Channels
There are no channels for the `smtp` thing.
-The `imap` and `pop3` things can be extended with `mailcount`-type channels.
+The `imap` and `pop3` things can be extended with `mailcount`- and `content`-type channels.
### Type `mailcount`
Each channel has two parameters: `folder` and `type`.
+
The `folder` is mandatory and denotes the folder name on the given account.
-You can either use the root folder like (e.g. "INBOX") or a sub directory of your structure (e.g. "INBOX.Sent" or "INBOX.Junk").
+
+You can either use the root folder like (e.g. "INBOX") or a subdirectory of your structure (e.g. "INBOX.Sent" or "INBOX.Junk").
The `type` parameter can be `UNREAD` or `TOTAL` (default).
Channels with type `UNREAD` give the number on unread mails in that folder.
+### Type `content`
+
+The `content` type channel presents the contents of an unread mail.
+If the message is a MIME- or MIME-multipart message, all parts are concatenated.
+The content is converted to a plain string without processing (i.e. HTML tags are still present).
+In most cases the mail content needs further processing in rules to trigger appropriate action.
+
+Each channel has five parameters: `folder`, `subject`, `sender`, `transformation` and `markAsRead`.
+
+The `folder` is mandatory and denotes the folder name on the given account.
+You can either use the root folder like (e.g. "INBOX") or a subdirectory of your structure (e.g. "INBOX.Sent" or "INBOX.Junk").
+
+`subject` and `sender` can be used to filter the messages that are processed by the channel.
+Filters use regular expressions (e.g. `.*DHL.*` as `sender` would match all From-addresses that contain "DHL").
+If a parameter is left empty, no filter is applied.
+
+The `transformation` is applied before setting the channel status.
+Transformations can be chained by separating them with the mathematical intersection character "∩", e.g. `REGEX:.*Shipment-Status: ([a-z]+).*∩MAP:status.map` would first extract a character string with a regular expression and then apply the given MAP transformation on the result.
+Please note that the values will be discarded if one transformation fails (e.g. REGEX did not match).
+This means that you can also use it to filter certain emails e.g. `REGEX:(.*Sendungsbenachrichtigung.*)` would only match for mails containing the string "Sendungsbenachrichtigung" but output the whole message.
+
+Since with each refresh all unread mails are processed the same message content would be sent to the channel multiple times.
+This can be prevented by setting `markAsRead` to `true` (default is `false`), which marks all processed messages as read.
+
## Full Example
mail.things:
Channels:
Type mailcount : inbox_total [ folder="INBOX", type="TOTAL" ]
Type mailcount : inbox_unread [ folder="INBOX", type="UNREAD" ]
+ Type content : fedex_notification [ folder="INBOX" sender="Fedex.*" markAsRead="true" ]
}
```
```java
Number InboxTotal "INBOX [%d]" { channel="mail:imap:sampleimap:inbox_total" }
Number InboxUnread "INBOX Unread [%d]" { channel="mail:imap:sampleimap:inbox_unread" }
+String FedexNotification { channel="mail:imap:sampleimap:fedex_notification" }
```
mail.sitemap:
Arrays.asList(THING_TYPE_SMTPSERVER, THING_TYPE_IMAPSERVER, THING_TYPE_POP3SERVER));
public static final ChannelTypeUID CHANNEL_TYPE_UID_FOLDER_MAILCOUNT = new ChannelTypeUID(BINDING_ID, "mailcount");
+ public static final ChannelTypeUID CHANNEL_TYPE_UID_MAIL_CONTENT = new ChannelTypeUID(BINDING_ID, "content");
}
*/
package org.openhab.binding.mail.internal;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link MailCountChannelType} enum for folder mail count type
*
* @author Jan N. Klug - Initial contribution
*/
-
+@NonNullByDefault
public enum MailCountChannelType {
UNREAD,
TOTAL
*
* @author Jan N. Klug - Initial contribution
*/
-@NonNullByDefault
@Component(configurationPid = "binding.mail", service = ThingHandlerFactory.class)
+@NonNullByDefault
public class MailHandlerFactory extends BaseThingHandlerFactory {
@Override
package org.openhab.binding.mail.internal;
import static org.openhab.binding.mail.internal.MailBindingConstants.CHANNEL_TYPE_UID_FOLDER_MAILCOUNT;
+import static org.openhab.binding.mail.internal.MailBindingConstants.CHANNEL_TYPE_UID_MAIL_CONTENT;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.Folder;
+import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
import javax.mail.search.FlagTerm;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.mail.internal.config.POP3IMAPChannelConfig;
import org.openhab.binding.mail.internal.config.POP3IMAPConfig;
+import org.openhab.binding.mail.internal.config.POP3IMAPContentChannelConfig;
+import org.openhab.binding.mail.internal.config.POP3IMAPMailCountChannelConfig;
import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.generic.ChannelTransformation;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
if (config.port == 0) {
switch (protocol) {
- case "imap":
- config.port = 143;
- break;
- case "imaps":
- config.port = 993;
- break;
- case "pop3":
- config.port = 110;
- break;
- case "pop3s":
- config.port = 995;
- break;
- default:
+ case "imap" -> config.port = 143;
+ case "imaps" -> config.port = 993;
+ case "pop3" -> config.port = 110;
+ case "pop3s" -> config.port = 995;
+ default -> {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
return;
+ }
}
}
}
private void refresh() {
+ if (Thread.currentThread().isInterrupted()) {
+ return;
+ }
Properties props = new Properties();
props.setProperty("mail." + baseProtocol + ".starttls.enable", "true");
props.setProperty("mail.store.protocol", protocol);
for (Channel channel : thing.getChannels()) {
if (CHANNEL_TYPE_UID_FOLDER_MAILCOUNT.equals(channel.getChannelTypeUID())) {
- final POP3IMAPChannelConfig channelConfig = channel.getConfiguration()
- .as(POP3IMAPChannelConfig.class);
+ final POP3IMAPMailCountChannelConfig channelConfig = channel.getConfiguration()
+ .as(POP3IMAPMailCountChannelConfig.class);
final String folderName = channelConfig.folder;
if (folderName == null || folderName.isEmpty()) {
logger.info("missing or empty folder name in channel {}", channel.getUID());
updateState(channel.getUID(), new DecimalType(
mailbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)).length));
}
- } catch (MessagingException e) {
- throw e;
+ }
+ }
+ } else if (CHANNEL_TYPE_UID_MAIL_CONTENT.equals(channel.getChannelTypeUID())) {
+ final POP3IMAPContentChannelConfig channelConfig = channel.getConfiguration()
+ .as(POP3IMAPContentChannelConfig.class);
+ final String folderName = channelConfig.folder;
+ if (folderName == null || folderName.isEmpty()) {
+ logger.info("missing or empty folder name in channel '{}'", channel.getUID());
+ } else {
+ try (Folder mailbox = store.getFolder(folderName)) {
+ mailbox.open(channelConfig.markAsRead ? Folder.READ_WRITE : Folder.READ_ONLY);
+ Message[] messages = mailbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
+ for (Message message : messages) {
+ String subject = message.getSubject();
+ Address[] senders = message.getFrom();
+ String sender = senders == null ? ""
+ : Stream.of(senders).map(Address::toString).collect(Collectors.joining(","));
+ logger.debug("Processing `{}` from `{}`", subject, sender);
+ if (!channelConfig.subject.isBlank() && !subject.matches(channelConfig.subject)) {
+ logger.trace("Subject '{}' did not pass subject filter", subject);
+ continue;
+ }
+ if (!channelConfig.sender.isBlank() && !sender.matches(channelConfig.sender)) {
+ logger.trace("Sender '{}' did not pass filter '{}'", subject, channelConfig.sender);
+ continue;
+ }
+ Object rawContent = message.getContent();
+ String contentAsString;
+ if (rawContent instanceof String) {
+ logger.trace("Detected plain text message");
+ contentAsString = (String) rawContent;
+ } else if (rawContent instanceof MimeMessage mimeMessage) {
+ logger.trace("Detected MIME message");
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ mimeMessage.writeTo(os);
+ contentAsString = os.toString();
+ }
+ } else if (rawContent instanceof MimeMultipart mimeMultipart) {
+ logger.trace("Detected MIME multipart message");
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ mimeMultipart.writeTo(os);
+ contentAsString = os.toString();
+ }
+ } else {
+ logger.warn(
+ "Failed to convert mail content from '{}' with subject '{}', to String: {}",
+ sender, subject, rawContent.getClass());
+ continue;
+ }
+ logger.trace("Found content '{}'", contentAsString);
+ new ChannelTransformation(channelConfig.transformation).apply(contentAsString)
+ .ifPresent(result -> updateState(channel.getUID(), new StringType(result)));
+ }
}
}
}
}
- } catch (MessagingException e) {
- logger.info("error when trying to refresh IMAP: {}", e.getMessage());
+ } catch (MessagingException | IOException e) {
+ logger.info("Failed refreshing IMAP for thing '{}': {}", thing.getUID(), e.getMessage());
}
}
}
*/
package org.openhab.binding.mail.internal;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link ServerSecurity} enum contains security configuration options
*
* @author Jan N. Klug - Initial contribution
*/
-
+@NonNullByDefault
public enum ServerSecurity {
PLAIN,
SSL,
*
* @author Jan N. Klug - Initial contribution
*/
-
@NonNullByDefault
public class BaseConfig {
public @Nullable String hostname;
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mail.internal.config;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.mail.internal.MailCountChannelType;
-
-/**
- * The {@link POP3IMAPChannelConfig} class contains fields mapping thing configuration parameters.
- *
- * @author Jan N. Klug - Initial contribution
- */
-
-@NonNullByDefault
-public class POP3IMAPChannelConfig {
- public @Nullable String folder;
- public MailCountChannelType type = MailCountChannelType.TOTAL;
-}
*
* @author Jan N. Klug - Initial contribution
*/
-
@NonNullByDefault
public class POP3IMAPConfig extends BaseConfig {
public int refresh = 60;
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mail.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link POP3IMAPContentChannelConfig} class contains fields mapping thing configuration parameters.
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@NonNullByDefault
+public class POP3IMAPContentChannelConfig {
+ public @Nullable String folder;
+ public String subject = "";
+ public String sender = "";
+ public @Nullable String transformation;
+
+ public boolean markAsRead = false;
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mail.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mail.internal.MailCountChannelType;
+
+/**
+ * The {@link POP3IMAPMailCountChannelConfig} class contains fields mapping thing configuration parameters.
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@NonNullByDefault
+public class POP3IMAPMailCountChannelConfig {
+ public @Nullable String folder;
+ public MailCountChannelType type = MailCountChannelType.TOTAL;
+}
*
* @author Jan N. Klug - Initial contribution
*/
-
@NonNullByDefault
public class SMTPConfig extends BaseConfig {
public @Nullable String sender;
thing-type.config.mail.smtp.sender.label = Sender
thing-type.config.mail.smtp.sender.description = Default sender address for mail
-config.hostname.label = Server Hostname
-config.password.label = SMTP Server Password
-config.port.label = Server Port
-config.refresh.label = Refresh Time
-config.refresh.description = Refresh time for this account in seconds
-config.security.label = SMTP Server Security Protocol
-config.security.option.PLAIN = plain
-config.security.option.STARTTLS = STARTTLS
-config.security.option.SSL = SSL/TLS
-config.username.label = SMTP Server Username
-
# channel types
+channel-type.mail.content.label = Content
+channel-type.mail.content.description = Mail content as String (with subject filter and content transformation).
channel-type.mail.mailcount.label = Mail Count
channel-type.mail.mailcount.description = Number of emails in folder
# channel types config
+channel-type.config.mail.content.folder.label = Folder Name
+channel-type.config.mail.content.markAsRead.label = Mark As Read
+channel-type.config.mail.content.markAsRead.description = Mark a processed mail as read and prevent further processing.
+channel-type.config.mail.content.sender.label = Sender Filter
+channel-type.config.mail.content.sender.description = A (regular expression) filter for the mail sender address.
+channel-type.config.mail.content.subject.label = Subject Filter
+channel-type.config.mail.content.subject.description = A (regular expression) filter for the mail subject.
+channel-type.config.mail.content.transformation.label = Transformation
+channel-type.config.mail.content.transformation.description = Transformation pattern used when processing messages. Multiple transformation can be chained using "∩".
channel-type.config.mail.mailcount.folder.label = Folder Name
channel-type.config.mail.mailcount.type.label = Counter Type
channel-type.config.mail.mailcount.type.option.UNREAD = Unread
channel-type.config.mail.mailcount.type.option.TOTAL = Total
+# thing types config
+
+config.hostname.label = Server Hostname
+config.password.label = SMTP Server Password
+config.port.label = Server Port
+config.refresh.label = Refresh Time
+config.refresh.description = Refresh time for this account in seconds
+config.security.label = SMTP Server Security Protocol
+config.security.option.PLAIN = plain
+config.security.option.STARTTLS = STARTTLS
+config.security.option.SSL = SSL/TLS
+config.username.label = SMTP Server Username
+
# actions
addHeaderActionLabel = add a mail header
<parameter name="hostname" type="text" required="true">
<label>@text/config.hostname.label</label>
</parameter>
- <parameter name="port" type="text" required="false">
+ <parameter name="port" type="text">
<label>@text/config.port.label</label>
<description>Default values are 25 for plain/STARTTLS and 465 for SSL/TLS</description>
<advanced>true</advanced>
</parameter>
- <parameter name="security" type="text" required="false">
+ <parameter name="security" type="text">
<label>@text/config.security.label</label>
<options>
<option value="PLAIN">@text/config.security.option.PLAIN</option>
<limitToOptions>true</limitToOptions>
<default>PLAIN</default>
</parameter>
- <parameter name="username" type="text" required="false">
+ <parameter name="username" type="text">
<label>@text/config.username.label</label>
</parameter>
- <parameter name="password" type="text" required="false">
+ <parameter name="password" type="text">
<label>@text/config.password.label</label>
<context>password</context>
</parameter>
</config-description>
</thing-type>
- <thing-type id="imap" extensible="mailcount">
+ <thing-type id="imap" extensible="mailcount,content">
<label>IMAP Server</label>
<description>Used for receiving emails</description>
<config-description>
<parameter name="hostname" type="text" required="true">
<label>@text/config.hostname.label</label>
</parameter>
- <parameter name="port" type="text" required="false">
+ <parameter name="port" type="text">
<label>@text/config.port.label</label>
<description>Default values are 143 for plain/STARTTLS and 993 for SSL/TLS</description>
<advanced>true</advanced>
</parameter>
- <parameter name="security" type="text" required="false">
+ <parameter name="security" type="text">
<label>@text/config.security.label</label>
<options>
<option value="PLAIN">@text/config.security.option.PLAIN</option>
<label>@text/config.password.label</label>
<context>password</context>
</parameter>
- <parameter name="refresh" type="integer" required="false">
+ <parameter name="refresh" type="integer">
<label>@text/config.refresh.label</label>
<description>@text/config.refresh.description</description>
<default>60</default>
</parameter>
</config-description>
</thing-type>
- <thing-type id="pop3" extensible="mailcount">
+ <thing-type id="pop3" extensible="mailcount,content">
<label>POP3 Server</label>
<description>Used for receiving emails</description>
<config-description>
<parameter name="hostname" type="text" required="true">
<label>@text/config.hostname.label</label>
</parameter>
- <parameter name="port" type="text" required="false">
+ <parameter name="port" type="text">
<label>@text/config.port.label</label>
<description>Default values are 110 for plain/STARTTLS and 995 for SSL/TLS</description>
<advanced>true</advanced>
</parameter>
- <parameter name="security" type="text" required="false">
+ <parameter name="security" type="text">
<label>@text/config.security.label</label>
<options>
<option value="PLAIN">@text/config.security.option.PLAIN</option>
<label>@text/config.password.label</label>
<context>password</context>
</parameter>
- <parameter name="refresh" type="integer" required="false">
+ <parameter name="refresh" type="integer">
<label>@text/config.refresh.label</label>
<description>@text/config.refresh.description</description>
<default>60</default>
<parameter name="folder" type="text" required="true">
<label>Folder Name</label>
</parameter>
- <parameter name="type" type="text" required="false">
+ <parameter name="type" type="text">
<label>Counter Type</label>
<options>
<option value="UNREAD">Unread</option>
</parameter>
</config-description>
</channel-type>
+
+ <channel-type id="content">
+ <item-type>String</item-type>
+ <label>Content</label>
+ <description>Mail content as String (with subject filter and content transformation).</description>
+ <state readOnly="true"/>
+ <config-description>
+ <parameter name="folder" type="text" required="true">
+ <label>Folder Name</label>
+ </parameter>
+ <parameter name="subject" type="text">
+ <label>Subject Filter</label>
+ <description>A (regular expression) filter for the mail subject.</description>
+ </parameter>
+ <parameter name="sender" type="text">
+ <label>Sender Filter</label>
+ <description>A (regular expression) filter for the mail sender address.</description>
+ </parameter>
+ <parameter name="transformation" type="text">
+ <label>Transformation</label>
+ <description>Transformation pattern used when processing messages. Multiple transformation can be chained using "∩".</description>
+ </parameter>
+ <parameter name="markAsRead" type="boolean">
+ <label>Mark As Read</label>
+ <description>Mark a processed mail as read and prevent further processing.</description>
+ <default>false</default>
+ </parameter>
+ </config-description>
+ </channel-type>
</thing:thing-descriptions>
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.mail;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.nio.file.Path;
-import java.util.Map;
-
-import javax.mail.MessagingException;
-import javax.mail.internet.AddressException;
-
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
-import org.apache.commons.mail.HtmlEmail;
-import org.apache.commons.mail.MultiPartEmail;
-import org.apache.commons.mail.SimpleEmail;
-import org.junit.jupiter.api.Test;
-import org.openhab.binding.mail.internal.MailBuilder;
-
-/**
- * The {@link MailBuilderTest} class defines tests for the {@link MailBuilder} class
- *
- * @author Jan N. Klug - Initial contribution
- */
-
-public class MailBuilderTest {
-
- private static final String TEST_STRING = "test";
- private static final String TEST_EMAIL = "foo@bar.zinga";
-
- private static final String HEADER_1_KEY = "key_one";
- private static final String HEADER_1_VAL = "value_one";
- private static final String HEADER_2_KEY = "key_two";
- private static final String HEADER_2_VAL = "value_two";
-
- @Test
- public void illegalToAddressThrowsException() {
- assertThrows(AddressException.class, () -> new MailBuilder("foo bar.zinga"));
- }
-
- @Test
- public void illegalFromAddressThrowsException() {
- assertThrows(EmailException.class, () -> new MailBuilder("TEST_EMAIL").withSender("foo bar.zinga").build());
- }
-
- @Test
- public void illegalURLThrowsException() {
- assertThrows(MalformedURLException.class,
- () -> new MailBuilder("TEST_EMAIL").withURLAttachment("foo bar.zinga"));
- }
-
- @Test
- public void withTextOnlyReturnsSimpleEmail() throws AddressException, EmailException {
- MailBuilder builder = new MailBuilder(TEST_EMAIL);
- Email mail = builder.withText("boo").build();
- assertThat(mail, instanceOf(SimpleEmail.class));
- }
-
- @Test
- public void withURLAttachmentReturnsMultiPartEmail()
- throws AddressException, EmailException, MalformedURLException {
- MailBuilder builder = new MailBuilder(TEST_EMAIL);
- String url = Path.of("src/test/resources/attachment.txt").toUri().toURL().toString();
- Email mail = builder.withText("boo").withURLAttachment(url).build();
- assertThat(mail, instanceOf(MultiPartEmail.class));
- }
-
- @Test
- public void withHtmlReturnsHtmlEmail() throws AddressException, EmailException {
- MailBuilder builder = new MailBuilder(TEST_EMAIL);
- Email mail = builder.withHtml("<html>test</html>").build();
- assertThat(mail, instanceOf(HtmlEmail.class));
- }
-
- @Test
- public void fieldsSetInMail() throws EmailException, MessagingException, IOException {
- MailBuilder builder = new MailBuilder(TEST_EMAIL);
-
- assertEquals("(no subject)", builder.build().getSubject());
- assertEquals(TEST_STRING, builder.withSubject(TEST_STRING).build().getSubject());
-
- assertEquals(TEST_EMAIL, builder.withSender(TEST_EMAIL).build().getFromAddress().getAddress());
-
- assertEquals(TEST_EMAIL, builder.build().getToAddresses().get(0).getAddress());
- assertEquals(2, builder.withRecipients(TEST_EMAIL).build().getToAddresses().size());
- }
-
- @Test
- public void withHeaders() throws EmailException, MessagingException, IOException {
- MailBuilder builder = new MailBuilder(TEST_EMAIL);
- Email mail = builder.withHeader(HEADER_1_KEY, HEADER_1_VAL).withHeader(HEADER_2_KEY, HEADER_2_VAL).build();
-
- Map<String, String> headers = mail.getHeaders();
-
- assertEquals(2, headers.size());
- assertEquals(HEADER_2_VAL, headers.get(HEADER_2_KEY));
- assertEquals(HEADER_1_VAL, headers.get(HEADER_1_KEY));
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.mail.internal;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.Path;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+
+import org.apache.commons.mail.Email;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.HtmlEmail;
+import org.apache.commons.mail.MultiPartEmail;
+import org.apache.commons.mail.SimpleEmail;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The {@link MailBuilderTest} class defines tests for the {@link MailBuilder} class
+ *
+ * @author Jan N. Klug - Initial contribution
+ */
+@NonNullByDefault
+public class MailBuilderTest {
+
+ private static final String TEST_STRING = "test";
+ private static final String TEST_EMAIL = "foo@bar.zinga";
+
+ private static final String HEADER_1_KEY = "key_one";
+ private static final String HEADER_1_VAL = "value_one";
+ private static final String HEADER_2_KEY = "key_two";
+ private static final String HEADER_2_VAL = "value_two";
+
+ @Test
+ public void illegalToAddressThrowsException() {
+ assertThrows(AddressException.class, () -> new MailBuilder("foo bar.zinga"));
+ }
+
+ @Test
+ public void illegalFromAddressThrowsException() {
+ assertThrows(EmailException.class, () -> new MailBuilder("TEST_EMAIL").withSender("foo bar.zinga").build());
+ }
+
+ @Test
+ public void illegalURLThrowsException() {
+ assertThrows(MalformedURLException.class,
+ () -> new MailBuilder("TEST_EMAIL").withURLAttachment("foo bar.zinga"));
+ }
+
+ @Test
+ public void withTextOnlyReturnsSimpleEmail() throws AddressException, EmailException {
+ MailBuilder builder = new MailBuilder(TEST_EMAIL);
+ Email mail = builder.withText("boo").build();
+ assertThat(mail, instanceOf(SimpleEmail.class));
+ }
+
+ @Test
+ public void withURLAttachmentReturnsMultiPartEmail()
+ throws AddressException, EmailException, MalformedURLException {
+ MailBuilder builder = new MailBuilder(TEST_EMAIL);
+ String url = Path.of("src/test/resources/attachment.txt").toUri().toURL().toString();
+ Email mail = builder.withText("boo").withURLAttachment(url).build();
+ assertThat(mail, instanceOf(MultiPartEmail.class));
+ }
+
+ @Test
+ public void withHtmlReturnsHtmlEmail() throws AddressException, EmailException {
+ MailBuilder builder = new MailBuilder(TEST_EMAIL);
+ Email mail = builder.withHtml("<html>test</html>").build();
+ assertThat(mail, instanceOf(HtmlEmail.class));
+ }
+
+ @Test
+ public void fieldsSetInMail() throws EmailException, MessagingException, IOException {
+ MailBuilder builder = new MailBuilder(TEST_EMAIL);
+
+ assertEquals("(no subject)", builder.build().getSubject());
+ assertEquals(TEST_STRING, builder.withSubject(TEST_STRING).build().getSubject());
+
+ assertEquals(TEST_EMAIL, builder.withSender(TEST_EMAIL).build().getFromAddress().getAddress());
+
+ assertEquals(TEST_EMAIL, builder.build().getToAddresses().get(0).getAddress());
+ assertEquals(2, builder.withRecipients(TEST_EMAIL).build().getToAddresses().size());
+ }
+
+ @Test
+ public void withHeaders() throws EmailException, MessagingException, IOException {
+ MailBuilder builder = new MailBuilder(TEST_EMAIL);
+ Email mail = builder.withHeader(HEADER_1_KEY, HEADER_1_VAL).withHeader(HEADER_2_KEY, HEADER_2_VAL).build();
+
+ Map<String, String> headers = mail.getHeaders();
+
+ assertEquals(2, headers.size());
+ assertEquals(HEADER_2_VAL, headers.get(HEADER_2_KEY));
+ assertEquals(HEADER_1_VAL, headers.get(HEADER_1_KEY));
+ }
+}
<suppress files=".+org.openhab.binding.yeelight.+" checks="OutsideOfLibExternalLibrariesCheck" />
<!-- suppress header checks for imported and patched apache commons-io files in logreader binding -->
<suppress files=".+org.openhab.binding.logreader.internal.thirdparty.commonsio.+" checks="ParameterizedRegexpHeaderCheck|AuthorTagCheck" />
+ <!-- Mail: Do not check org.apache.commons.mail usage -->
+ <suppress files=".+[\\/]mail[\\/].+[\\/]MailBuilder\.java" checks="ForbiddenPackageUsageCheck"/>
+ <suppress files=".+[\\/]mail[\\/].+[\\/]MailBuilderTest\.java" checks="ForbiddenPackageUsageCheck"/>
+ <suppress files=".+[\\/]mail[\\/].+[\\/]SMTPHandler\.java" checks="ForbiddenPackageUsageCheck"/>
+ <suppress files=".+[\\/]mail[\\/].+[\\/]SendMailActions\.java" checks="ForbiddenPackageUsageCheck"/>
</suppressions>