diff --git a/.circleci/config.yml b/.circleci/config.yml
index c4ab4a3..c44e234 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,6 +4,8 @@
# Environment variables: https://circleci.com/docs/2.0/env-vars
# Verify circleci *.yml: https://circleci.com/docs/2.0/local-cli
#
+# @todo #/DEV Enable `qulice` plugin once https://github.com/teamed/qulice/issues/1035 is resolved.
+#
version: 2
jobs:
assemble_jar:
@@ -20,7 +22,7 @@ jobs:
- run:
name: Build java sources (including integration tests)
command: |
- mvn -X -P integration-tests,qulice clean install -DLL.yandex.user=${EMAIL_USER} -DLL.yandex.pass=${EMAIL_PASS} -DLL.yandex.to.user=${EMAIL_TO}
+ mvn -X -P integration-tests clean install -DLL.yandex.user=${EMAIL_USER} -DLL.yandex.pass=${EMAIL_PASS} -DLL.yandex.to.user=${EMAIL_TO}
workflows:
version: 2
diff --git a/.gitignore b/.gitignore
index 92ad7a8..f3eaaf6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
*.iml
*.iws
*.class
-/.idea/
\ No newline at end of file
+/.idea/
+/.tmp/
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0ef3b7c..d04e33f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,9 +31,7 @@
0.0.0
jar
${project.artifactId}
-
- Simplify manipulations with CLI terminal(s) for Java-based applications
-
+ Simplify manipulations with CLI terminal(s) for Java-based applications
http://github.com/dgroup/mbox4j
@@ -61,9 +59,7 @@
${project.version}
MIT License
-
- https://github.com/dgroup/mbox4j/blob/master/license.txt
-
+ https://github.com/dgroup/mbox4j/blob/master/license.txt
1.8
UTF-8
@@ -80,7 +76,7 @@
1.5
1.0.0
0.7.9
- 0.18.9
+ 0.18.13
3.4.0.905
1.2
diff --git a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/AttachmentOf.java b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/AttachmentOf.java
new file mode 100644
index 0000000..08286bc
--- /dev/null
+++ b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/AttachmentOf.java
@@ -0,0 +1,90 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Yurii Dubinka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+ * OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package io.github.dgroup.mbox4j.inbox.javax;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import javax.mail.Part;
+import org.cactoos.Scalar;
+import org.cactoos.io.InputOf;
+import org.cactoos.io.OutputTo;
+import org.cactoos.io.TeeInput;
+import org.cactoos.scalar.LengthOf;
+
+/**
+ * Represents an attachment from {@link javax.mail.Part}.
+ *
+ * @since 0.1.0
+ */
+public final class AttachmentOf implements Scalar {
+
+ /**
+ * An instance of {@link javax.mail.Part} with attachment.
+ */
+ private final Part part;
+
+ /**
+ * The temporal directory for the email attachments.
+ */
+ private final Scalar tmp;
+
+ /**
+ * Ctor.
+ * @param part An instance of {@link javax.mail.Part} with attachment.
+ * @param tmp The temporal directory for the email attachments.
+ */
+ public AttachmentOf(final Part part, final Scalar tmp) {
+ this.part = part;
+ this.tmp = tmp;
+ }
+
+ @Override
+ @SuppressWarnings("PMD.AvoidCatchingGenericException")
+ public File value() throws IOException {
+ try {
+ final Path dest = Paths.get(
+ this.tmp.value().getAbsolutePath(), this.part.getFileName()
+ );
+ try (BufferedInputStream inp = new BufferedInputStream(this.part.getInputStream());
+ BufferedOutputStream out = new BufferedOutputStream(Files.newOutputStream(dest))) {
+ new LengthOf(
+ new TeeInput(
+ new InputOf(inp),
+ new OutputTo(out)
+ )
+ ).intValue();
+ }
+ return dest.toFile();
+ // @checkstyle IllegalCatchCheck (3 lines)
+ } catch (final Exception cause) {
+ throw new IOException(cause);
+ }
+ }
+}
diff --git a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/PartsOf.java b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/PartsOf.java
new file mode 100644
index 0000000..1798467
--- /dev/null
+++ b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/PartsOf.java
@@ -0,0 +1,59 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Yurii Dubinka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+ * OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package io.github.dgroup.mbox4j.inbox.javax;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import javax.mail.Multipart;
+import javax.mail.Part;
+import org.cactoos.iterable.IterableEnvelope;
+
+/**
+ * Represents all parts of {@link javax.mail.Multipart} as an {@link Iterable}.
+ *
+ * @since 0.1.0
+ * @todo #/DEV Add hierarchical unwrap as multipart can have tree-based structure.
+ * For example, the multipart can have few levels with multipart(s).
+ */
+public final class PartsOf extends IterableEnvelope {
+
+ /**
+ * Ctor.
+ * @param multipart The content of email message.
+ */
+ public PartsOf(final Multipart multipart) {
+ super(
+ () -> {
+ final int quantity = multipart.getCount();
+ final Collection parts = new ArrayList<>(quantity);
+ for (int idx = 0; idx < quantity; ++idx) {
+ parts.add(multipart.getBodyPart(idx));
+ }
+ return parts;
+ }
+ );
+ }
+
+}
diff --git a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/RecipientsOf.java b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/RecipientsOf.java
new file mode 100644
index 0000000..e0a384d
--- /dev/null
+++ b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/RecipientsOf.java
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Yurii Dubinka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+ * OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package io.github.dgroup.mbox4j.inbox.javax;
+
+import java.util.Collections;
+import java.util.Set;
+import javax.mail.Address;
+import javax.mail.Message;
+import org.cactoos.collection.Mapped;
+import org.cactoos.set.SetEnvelope;
+import org.cactoos.set.SetOf;
+
+/**
+ * The email recipients based on their type for the dedicated message.
+ *
+ * @since 0.1.0
+ */
+public final class RecipientsOf extends SetEnvelope {
+
+ /**
+ * Ctor.
+ * @param type The type of recipients.
+ * @param msg The message with recipients.
+ */
+ public RecipientsOf(final Message.RecipientType type, final Message msg) {
+ super(
+ () -> {
+ final Address[] addresses = msg.getRecipients(type);
+ final Set recipients;
+ if (addresses == null || addresses.length == 0) {
+ recipients = Collections.emptySet();
+ } else {
+ recipients = new SetOf<>(
+ new Mapped<>(Address::toString, addresses)
+ );
+ }
+ return recipients;
+ }
+ );
+ }
+}
diff --git a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/ToMsg.java b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/ToMsg.java
index c220fb3..b15ca6d 100644
--- a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/ToMsg.java
+++ b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/ToMsg.java
@@ -26,14 +26,15 @@
import io.github.dgroup.mbox4j.Msg;
import io.github.dgroup.mbox4j.msg.MsgOf;
-import java.util.Collections;
-import java.util.Set;
-import javax.mail.Address;
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
import javax.mail.Message;
import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Part;
import org.cactoos.Func;
-import org.cactoos.collection.Mapped;
-import org.cactoos.set.SetOf;
+import org.cactoos.Scalar;
/**
* The function to map {@link javax.mail.Message} and {@link Msg}.
@@ -43,43 +44,86 @@
* text-based thus we need to define a way how to send the files.
* For now Collections.emptySet is used as a stub and should be
* removed later.
- * @todo #/DEV Message content: transform MimeMultipart to String
- * For now the body content msg.getContent().toString()
- * looks as ... body=javax.mail.internet.MimeMultipart@35a50a4c ....
*/
public final class ToMsg implements Func {
+ /**
+ * The temporal directory for the email attachments.
+ */
+ private final Scalar tmp;
+
+ /**
+ * Ctor.
+ */
+ public ToMsg() {
+ this(() -> new File(".tmp"));
+ }
+
+ /**
+ * Ctor.
+ * @param tmp The temporal directory for the email attachments.
+ */
+ public ToMsg(final Scalar tmp) {
+ this.tmp = tmp;
+ }
+
@Override
public Msg apply(final Message msg) throws Exception {
+ this.createTemporalDirectoryIfAbsent();
+ final Object content = msg.getContent();
+ final StringBuilder body = new StringBuilder();
+ final Collection attachments = new LinkedList<>();
+ if (textPlain(msg)) {
+ body.append(content.toString());
+ } else if (content instanceof Multipart) {
+ final Multipart multipart = (Multipart) content;
+ for (final Part part : new PartsOf(multipart)) {
+ if (textPlain(part)) {
+ body.append(part.getContent().toString());
+ } else if (Part.ATTACHMENT.equals(part.getDisposition())) {
+ attachments.add(new AttachmentOf(part, this.tmp).value());
+ }
+ }
+ }
return new MsgOf(
msg.getFrom()[0].toString(),
- recipients(Message.RecipientType.TO, msg),
- recipients(Message.RecipientType.CC, msg),
- recipients(Message.RecipientType.BCC, msg),
+ new RecipientsOf(Message.RecipientType.TO, msg),
+ new RecipientsOf(Message.RecipientType.CC, msg),
+ new RecipientsOf(Message.RecipientType.BCC, msg),
msg.getSubject(),
- msg.getContent().toString(),
- Collections.emptySet()
+ body.toString(),
+ attachments
);
}
/**
- * Fetch the recipients based on their type from the message.
- * @param type The type of recipients.
- * @param msg The message with recipients.
- * @return The recipients.
- * @throws MessagingException In case if recipients can't be retrieved.
+ * Create the temporal directory for the attachments from the email.
+ * @throws Exception In case if temporal directory can't be detected
+ * or created.
*/
- private static Set recipients(final Message.RecipientType type, final Message msg)
- throws MessagingException {
- final Address[] addresses = msg.getRecipients(type);
- final Set recipients;
- if (addresses == null || addresses.length == 0) {
- recipients = Collections.emptySet();
- } else {
- recipients = new SetOf<>(
- new Mapped<>(Address::toString, addresses)
+ private void createTemporalDirectoryIfAbsent() throws Exception {
+ final File ftmp = this.tmp.value();
+ if (ftmp.exists() && ftmp.isDirectory()) {
+ return;
+ }
+ if (!ftmp.exists() && !ftmp.mkdir()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Can't initiate the '%s' as temporal directory for email storing",
+ ftmp.getAbsolutePath()
+ )
);
}
- return recipients;
+ }
+
+ /**
+ * Ensure that {@link javax.mail.Part} has content `text/plain`.
+ * @param part The original part.
+ * @return The true when type is matched.
+ * @throws MessagingException In the case of connectivity issues.
+ * @see Part#getContentType()
+ */
+ private static boolean textPlain(final Part part) throws MessagingException {
+ return part.getContentType().contains("text/plain");
}
}
diff --git a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/search/mode/All.java b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/search/mode/All.java
index f8fd728..ac6b620 100644
--- a/src/main/java/io/github/dgroup/mbox4j/inbox/javax/search/mode/All.java
+++ b/src/main/java/io/github/dgroup/mbox4j/inbox/javax/search/mode/All.java
@@ -28,6 +28,7 @@
import io.github.dgroup.mbox4j.inbox.javax.ToMsg;
import java.util.ArrayList;
import javax.mail.Folder;
+import javax.mail.Message;
import org.cactoos.Func;
import org.cactoos.collection.Mapped;
@@ -39,10 +40,28 @@
*/
public final class All implements Func> {
+ /**
+ * The function to map {@link javax.mail.Message} to {@link Msg}.
+ */
+ private final Func fnc;
+
+ /**
+ * Ctor.
+ */
+ public All() {
+ this(new ToMsg());
+ }
+
+ /**
+ * Ctor.
+ * @param fnc The function to map {@link javax.mail.Message} to {@link Msg}.
+ */
+ public All(final Func fnc) {
+ this.fnc = fnc;
+ }
+
@Override
public Iterable apply(final Folder folder) throws Exception {
- return new ArrayList<>(
- new Mapped<>(new ToMsg(), folder.getMessages())
- );
+ return new ArrayList<>(new Mapped<>(this.fnc, folder.getMessages()));
}
}
diff --git a/src/main/java/io/github/dgroup/mbox4j/outbox/javax/MimeMsg.java b/src/main/java/io/github/dgroup/mbox4j/outbox/javax/MimeMsg.java
index 5b66bc2..a189c0a 100644
--- a/src/main/java/io/github/dgroup/mbox4j/outbox/javax/MimeMsg.java
+++ b/src/main/java/io/github/dgroup/mbox4j/outbox/javax/MimeMsg.java
@@ -54,9 +54,13 @@ public Message apply(final Session session, final Msg msg) throws Exception {
email.setRecipients(Message.RecipientType.CC, new Addresses(msg.cc()).value());
email.setRecipients(Message.RecipientType.BCC, new Addresses(msg.bcc()).value());
email.setSubject(msg.subject().asString());
- email.setText(msg.body().asString());
- if (!msg.attachments().isEmpty()) {
+ if (msg.attachments().isEmpty()) {
+ email.setText(msg.body().asString());
+ } else {
final Multipart multipart = new MimeMultipart();
+ final MimeBodyPart text = new MimeBodyPart();
+ text.setText(msg.body().asString());
+ multipart.addBodyPart(text);
for (final File attachment : msg.attachments()) {
final MimeBodyPart part = new MimeBodyPart();
final DataSource source = new FileDataSource(attachment);
diff --git a/src/test/java/io/github/dgroup/mbox4j/inbox/javax/JavaxMailInboxTestIT.java b/src/test/java/io/github/dgroup/mbox4j/inbox/javax/JavaxMailInboxTestIT.java
index 03d4a60..4111de7 100644
--- a/src/test/java/io/github/dgroup/mbox4j/inbox/javax/JavaxMailInboxTestIT.java
+++ b/src/test/java/io/github/dgroup/mbox4j/inbox/javax/JavaxMailInboxTestIT.java
@@ -32,9 +32,11 @@
import io.github.dgroup.mbox4j.query.QueryOf;
import io.github.dgroup.mbox4j.query.mode.Mode;
import io.github.dgroup.mbox4j.query.mode.ModeOf;
+import java.io.File;
import java.util.ArrayList;
import javax.mail.Folder;
import org.cactoos.Func;
+import org.cactoos.collection.Joined;
import org.cactoos.collection.Mapped;
import org.cactoos.map.MapEntry;
import org.cactoos.map.MapOf;
@@ -58,7 +60,7 @@ public final class JavaxMailInboxTestIT {
public void size() {
new Assertion<>(
"3 emails were read",
- () -> this.read(3),
+ () -> this.read(1, 3),
Matchers.iterableWithSize(3)
).affirm();
}
@@ -69,7 +71,7 @@ public void subject() {
"3 emails have expected subject",
() -> new Mapped<>(
msg -> msg.subject().asString(),
- this.read(3)
+ this.read(1, 3)
),
new HasValues<>(
"The first email",
@@ -79,18 +81,33 @@ public void subject() {
).affirm();
}
+ @Test
+ public void attachments() {
+ new Assertion<>(
+ "2 file names from email has expected names",
+ () -> new Mapped<>(
+ File::getName,
+ new Joined<>(
+ new Mapped<>(Msg::attachments, this.read(4, 4))
+ )
+ ),
+ new HasValues<>(".gitignore", ".pdd")
+ ).affirm();
+ }
+
/**
* Read range of emails from the dedicated SMTP server.
- * @param quantity The quantity of messages to be fetched from SMTP server.
+ * @param start
+ * @param end
* @return The emails.
* @throws EmailException In the case of connectivity issues.
*/
- private Iterable read(final int quantity) throws EmailException {
+ private Iterable read(final int start, final int end) throws EmailException {
final Mode mode = new ModeOf("first several emails");
final Query query = new QueryOf("imaps", "INBOX", mode);
final Func> fnc = folder -> new ArrayList<>(
new Mapped<>(
- new ToMsg(), folder.getMessages(1, quantity)
+ new ToMsg(), folder.getMessages(start, end)
)
);
return new JavaxMailInbox(
@@ -98,4 +115,5 @@ private Iterable read(final int quantity) throws EmailException {
new Modes(new MapOf<>(new MapEntry<>(mode, fnc)))
).read(query);
}
+
}