From 089a17d6a238214e7bacf9bdc06325f9caaf7416 Mon Sep 17 00:00:00 2001 From: Ondra Pelech Date: Sun, 6 Feb 2022 00:57:44 +0100 Subject: [PATCH 1/3] Support for Gmail labels (#329) * Gmail * Gmail 2 * Gmail * Gmail * fix * smtp --- .../src/main/scala/emil/AccessImap.scala | 17 +++++++--- .../scala/emil/javamail/JavaMailEmil.scala | 2 +- .../javamail/internal/AccessGimapImpl.scala | 32 +++++++++++++++++++ .../javamail/internal/AccessImapImpl.scala | 10 +++--- .../internal/ConnectionResource.scala | 5 +-- .../emil/javamail/internal/GmailLabel.scala | 3 ++ .../emil/javamail/internal/ops/FindMail.scala | 23 ++++++------- .../emil/javamail/internal/ops/LoadMail.scala | 14 ++++---- .../javamail/internal/ops/LoadMailRaw.scala | 31 ++++++++++-------- .../emil/javamail/internal/ops/MoveMail.scala | 32 ++++++++++++++++++- .../javamail/internal/ops/SearchMails.scala | 21 ++++++++++-- project/Dependencies.scala | 4 ++- 12 files changed, 146 insertions(+), 48 deletions(-) create mode 100644 modules/javamail/src/main/scala/emil/javamail/internal/AccessGimapImpl.scala create mode 100644 modules/javamail/src/main/scala/emil/javamail/internal/GmailLabel.scala diff --git a/modules/common/src/main/scala/emil/AccessImap.scala b/modules/common/src/main/scala/emil/AccessImap.scala index d955b2b6..358e70af 100644 --- a/modules/common/src/main/scala/emil/AccessImap.scala +++ b/modules/common/src/main/scala/emil/AccessImap.scala @@ -16,15 +16,24 @@ trait AccessImap[F[_], -C] extends Access[F, C] { end: MailUid ): MailOp[F, C, List[Mail[F]]] - def loadMail(folder: MailFolder, uids: List[MailUid]): MailOp[F, C, List[Mail[F]]] + def loadMail( + folder: MailFolder, + uids: Set[MailUid] + ): MailOp[F, C, List[Mail[F]]] - def loadMailRaw(folder: MailFolder, uid: MailUid): MailOp[F, C, Option[ByteVector]] + def loadMailRaw( + folder: MailFolder, + uid: MailUid + ): MailOp[F, C, Map[MailHeader, ByteVector]] def loadMailRaw( folder: MailFolder, start: MailUid, end: MailUid - ): MailOp[F, C, List[ByteVector]] + ): MailOp[F, C, Map[MailHeader, ByteVector]] - def loadMailRaw(folder: MailFolder, uids: List[MailUid]): MailOp[F, C, List[ByteVector]] + def loadMailRaw( + folder: MailFolder, + uids: Set[MailUid] + ): MailOp[F, C, Map[MailHeader, ByteVector]] } diff --git a/modules/javamail/src/main/scala/emil/javamail/JavaMailEmil.scala b/modules/javamail/src/main/scala/emil/javamail/JavaMailEmil.scala index d7774623..816303ae 100644 --- a/modules/javamail/src/main/scala/emil/javamail/JavaMailEmil.scala +++ b/modules/javamail/src/main/scala/emil/javamail/JavaMailEmil.scala @@ -29,7 +29,7 @@ final class JavaMailEmil[F[_]: Sync: ContextShift] private ( new SendImpl[F](blocker) def access: Access[F, JavaMailConnection] = - new AccessImapImpl[F](blocker) + new AccessGimapImpl[F](blocker) } object JavaMailEmil { diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/AccessGimapImpl.scala b/modules/javamail/src/main/scala/emil/javamail/internal/AccessGimapImpl.scala new file mode 100644 index 00000000..512f74fe --- /dev/null +++ b/modules/javamail/src/main/scala/emil/javamail/internal/AccessGimapImpl.scala @@ -0,0 +1,32 @@ +package emil.javamail.internal + +import cats.effect.{Blocker, ContextShift, Sync} +import com.sun.mail.gimap.{GmailFolder, GmailStore} +import emil.javamail.internal.BlockingSyntax._ +import emil.javamail.internal.ops.{MoveMail, SearchMails} +import emil.{AccessImap, MailHeader, MailOp} +import jakarta.mail.Transport + +class AccessGimapImpl[F[_]: Sync: ContextShift](blocker: Blocker) + extends AccessImapImpl[F](blocker) + with AccessImap[F, JavaMailConnectionGeneric[GmailStore, Transport, GmailFolder]] { + + def getGmailLabels(mh: MailHeader): MailOp[ + F, + JavaMailConnectionGeneric[GmailStore, Transport, GmailFolder], + Set[GmailLabel] + ] = + SearchMails.getGmailLabels(mh).blockOn(blocker) + + def setGmailLabels( + mh: MailHeader, + labels: Set[GmailLabel], + set: Boolean + ): MailOp[ + F, + JavaMailConnectionGeneric[GmailStore, Transport, GmailFolder], + Unit + ] = + MoveMail.setGmailLabels(mh, labels, set).blockOn(blocker) + +} diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/AccessImapImpl.scala b/modules/javamail/src/main/scala/emil/javamail/internal/AccessImapImpl.scala index 6e6a050f..61996b29 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/AccessImapImpl.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/AccessImapImpl.scala @@ -42,27 +42,27 @@ class AccessImapImpl[F[_]: Sync: ContextShift](blocker: Blocker) def loadMail( folder: MailFolder, - uids: List[MailUid] + uids: Set[MailUid] ): MailOp[F, JavaMailImapConnection, List[Mail[F]]] = LoadMail.byUid[F](folder, uids).blockOn(blocker) def loadMailRaw( folder: MailFolder, uid: MailUid - ): MailOp[F, JavaMailImapConnection, Option[ByteVector]] = + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = LoadMailRaw.byUid[F](folder, uid).blockOn(blocker) def loadMailRaw( folder: MailFolder, start: MailUid, end: MailUid - ): MailOp[F, JavaMailImapConnection, List[ByteVector]] = + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = LoadMailRaw.byUid[F](folder, start, end).blockOn(blocker) def loadMailRaw( folder: MailFolder, - uids: List[MailUid] - ): MailOp[F, JavaMailImapConnection, List[ByteVector]] = + uids: Set[MailUid] + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = LoadMailRaw.byUid[F](folder, uids).blockOn(blocker) } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ConnectionResource.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ConnectionResource.scala index 92a761a5..3fa5b30b 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ConnectionResource.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ConnectionResource.scala @@ -27,8 +27,9 @@ object ConnectionResource { def make[F[_]: Sync](mc: MailConfig, settings: Settings): F[JavaMailConnection] = Sync[F].delay { - val session = createSession(mc, settings) - if (mc.urlParts.protocol.toLowerCase.startsWith("imap")) { + val session = createSession(mc, settings) + val protocol = mc.urlParts.protocol.toLowerCase + if (protocol.startsWith("imap") || protocol.startsWith("gimap")) { val store = createImapStore(session, mc) JavaMailConnectionGeneric(mc, session, Some(store), None) } else { diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/GmailLabel.scala b/modules/javamail/src/main/scala/emil/javamail/internal/GmailLabel.scala new file mode 100644 index 00000000..5a3e7ba0 --- /dev/null +++ b/modules/javamail/src/main/scala/emil/javamail/internal/GmailLabel.scala @@ -0,0 +1,3 @@ +package emil.javamail.internal + +case class GmailLabel(value: String) diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/FindMail.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/FindMail.scala index c4cd7ae0..1b644579 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/FindMail.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/FindMail.scala @@ -1,7 +1,8 @@ package emil.javamail.internal.ops import cats.effect.Sync -import com.sun.mail.imap.IMAPFolder +import cats.syntax.all._ +import com.sun.mail.imap._ import emil._ import emil.javamail.internal._ import jakarta.mail.internet.MimeMessage @@ -22,13 +23,12 @@ object FindMail { def byUid[F[_]: Sync]( folder: MailFolder, uid: MailUid - ): MailOp[F, JavaMailImapConnection, Option[MimeMessage]] = MailOp { + ): MailOp[F, JavaMailImapConnection, Option[IMAPMessage]] = MailOp { _.folder[F](folder.id) .use { folder => Sync[F].delay { logger.debug(s"About to find mail $uid") - Option(folder.getMessageByUID(uid.n)) - .collect { case m: MimeMessage => m } + Option(folder.getMessageByUID(uid.n)).map(_.asInstanceOf[IMAPMessage]) } } } @@ -37,14 +37,14 @@ object FindMail { folder: MailFolder, start: MailUid, end: MailUid - ): MailOp[F, JavaMailImapConnection, List[MimeMessage]] = MailOp { + ): MailOp[F, JavaMailImapConnection, List[IMAPMessage]] = MailOp { _.folder[F](folder.id) .use { folder => Sync[F].delay { logger.debug(s"About to find mail from $start to $end") folder .getMessagesByUID(start.n, end.n) - .collect { case m: MimeMessage => m } + .map(_.asInstanceOf[IMAPMessage]) .toList } } @@ -52,15 +52,16 @@ object FindMail { def byUid[F[_]: Sync]( folder: MailFolder, - uids: List[MailUid] - ): MailOp[F, JavaMailImapConnection, List[MimeMessage]] = MailOp { + uids: Set[MailUid] + ): MailOp[F, JavaMailImapConnection, List[IMAPMessage]] = MailOp { _.folder[F](folder.id) .use { folder => Sync[F].delay { - logger.debug(s"About to find mail with $uids") + val uidsSorted = uids.toArray.sortBy(_.n) + logger.debug(s"About to find mail with ${uidsSorted.toList}") folder - .getMessagesByUID(uids.toArray.map(_.n)) - .collect { case m: MimeMessage => m } + .getMessagesByUID(uidsSorted.map(_.n)) + .map(_.asInstanceOf[IMAPMessage]) .toList } } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala index 1b451b1c..fa7b6b17 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala @@ -31,22 +31,22 @@ object LoadMail { def byUid[F[_]: Sync](folder: MailFolder, start: MailUid, end: MailUid)(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailImapConnection, List[Mail[F]]] = - FindMail.byUid[F](folder, start, end).map { optMime => + FindMail.byUid[F](folder, start, end).map { mimes => logger .debug( - s"Loaded complete raw mail from '$start' to '$end' from mime messages '$optMime'" + s"Loaded complete raw mail from '$start' to '$end' from mime messages: ${mimes.size}" ) - optMime.map(cm.convert) + mimes.map(cm.convert) } - def byUid[F[_]: Sync](folder: MailFolder, uids: List[MailUid])(implicit + def byUid[F[_]: Sync](folder: MailFolder, uids: Set[MailUid])(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailImapConnection, List[Mail[F]]] = - FindMail.byUid[F](folder, uids).map { optMime => + FindMail.byUid[F](folder, uids).map { mimes => logger .debug( - s"Loaded complete raw mail for '$uids' from mime messages '$optMime'" + s"Loaded complete raw mail for '$uids' from mime messages: ${mimes.size}" ) - optMime.map(cm.convert) + mimes.map(cm.convert) } } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala index b68832dd..16d61217 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala @@ -21,34 +21,37 @@ object LoadMailRaw { } def byUid[F[_]: Sync](folder: MailFolder, uid: MailUid)(implicit - cm: Conv[MimeMessage, ByteVector] - ): MailOp[F, JavaMailImapConnection, Option[ByteVector]] = + cm: Conv[MimeMessage, MailHeader], + cb: Conv[MimeMessage, ByteVector] + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = FindMail.byUid[F](folder, uid).map { optMime => logger .debug(s"Loaded complete raw mail for '$uid' from mime message '$optMime'") - optMime.map(cm.convert) + optMime.map(mime => cm.convert(mime) -> cb.convert(mime)).toList.toMap } def byUid[F[_]: Sync](folder: MailFolder, start: MailUid, end: MailUid)(implicit - cm: Conv[MimeMessage, ByteVector] - ): MailOp[F, JavaMailImapConnection, List[ByteVector]] = - FindMail.byUid[F](folder, start, end).map { optMime => + cm: Conv[MimeMessage, MailHeader], + cb: Conv[MimeMessage, ByteVector] + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = + FindMail.byUid[F](folder, start, end).map { mimes => logger .debug( - s"Loaded complete raw mail from '$start' to '$end' from mime messages '$optMime'" + s"Loaded complete raw mail from '$start' to '$end' from mime messages: ${mimes.size}" ) - optMime.map(cm.convert) + mimes.map(mime => cm.convert(mime) -> cb.convert(mime)).toMap } - def byUid[F[_]: Sync](folder: MailFolder, uids: List[MailUid])(implicit - cm: Conv[MimeMessage, ByteVector] - ): MailOp[F, JavaMailImapConnection, List[ByteVector]] = - FindMail.byUid[F](folder, uids).map { optMime => + def byUid[F[_]: Sync](folder: MailFolder, uids: Set[MailUid])(implicit + cm: Conv[MimeMessage, MailHeader], + cb: Conv[MimeMessage, ByteVector] + ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = + FindMail.byUid[F](folder, uids).map { mimes => logger .debug( - s"Loaded complete raw mail for '$uids' from mime messages '$optMime'" + s"Loaded complete raw mail for '$uids' from mime messages: ${mimes.size}" ) - optMime.map(cm.convert) + mimes.map(mime => cm.convert(mime) -> cb.convert(mime)).toMap } } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/MoveMail.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/MoveMail.scala index 40c3d2c1..4bd1f742 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/MoveMail.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/MoveMail.scala @@ -3,9 +3,10 @@ package emil.javamail.internal.ops import cats.data.Kleisli import cats.effect.Sync import cats.implicits._ +import com.sun.mail.gimap.{GmailFolder, GmailMessage, GmailStore} import com.sun.mail.imap.IMAPFolder import emil._ -import emil.javamail.internal.{JavaMailConnection, Logger, Util} +import emil.javamail.internal._ import jakarta.mail._ import jakarta.mail.internet.MimeMessage @@ -72,4 +73,33 @@ object MoveMail { source.expunge() () } + + def setGmailLabels[F[_]: Sync]( + mh: MailHeader, + labels: Set[GmailLabel], + set: Boolean + ): MailOp[ + F, + JavaMailConnectionGeneric[GmailStore, Transport, GmailFolder], + Unit + ] = + FindMail(mh).flatMap { mime => + MailOp { _ => + Sync[F].delay { + mime match { + case Some(mime) => + logger.debug( + s"Setting labels on message. header: $mh, labels: $labels" + ) + Util.withWriteFolder(mime.getFolder) { _ => + mime + .asInstanceOf[GmailMessage] + .setLabels(labels.toArray.map(_.value), set) + } + case None => + sys.error(s"Message not found. header: $mh") + } + } + } + } } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/SearchMails.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/SearchMails.scala index a50e236c..75da5fcd 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/SearchMails.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/SearchMails.scala @@ -1,12 +1,13 @@ package emil.javamail.internal.ops import cats.effect.Sync +import com.sun.mail.gimap.{GmailFolder, GmailMessage, GmailStore} import emil._ import emil.javamail.conv.Conv -import emil.javamail.internal.{JavaMailConnection, Logger} +import emil.javamail.internal._ import jakarta.mail.internet.MimeMessage import jakarta.mail.search.SearchTerm -import jakarta.mail.{Flags, Folder} +import jakarta.mail.{Flags, Folder, Transport} object SearchMails { private[this] val logger = Logger(getClass) @@ -68,4 +69,20 @@ object SearchMails { ) ) + def getGmailLabels[F[_]: Sync](mh: MailHeader): MailOp[ + F, + JavaMailConnectionGeneric[GmailStore, Transport, GmailFolder], + Set[GmailLabel] + ] = FindMail(mh).flatMapF { + case Some(mime) => + Sync[F].delay { + logger.debug(s"Getting labels for email. header: $mh") + mime.asInstanceOf[GmailMessage].getLabels.map(GmailLabel).toSet + } + case None => + Sync[F].raiseError( + new RuntimeException(s"No mail with given header found. header: $mh") + ) + } + } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4f170068..a0fb3c4c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -39,7 +39,9 @@ object Dependencies { ) val javaxMail = Seq( - "com.sun.mail" % "jakarta.mail" % javaxMailVersion + "com.sun.mail" % "imap" % javaxMailVersion, + "com.sun.mail" % "smtp" % javaxMailVersion, + "com.sun.mail" % "gimap" % javaxMailVersion ) val greenmail = Seq( From 94e61b166098af17dd56f81abffdbbc7900551fe Mon Sep 17 00:00:00 2001 From: havelkahonza Date: Fri, 18 Feb 2022 10:56:33 +0100 Subject: [PATCH 2/3] Fix bug in loading of raw messages, now downloads really whole, not just body (#336) Co-authored-by: Jan Havelka --- .../test/scala/emil/AbstractSendTest.scala | 23 +++++++++++++++++-- .../scala/emil/javamail/conv/BodyDecode.scala | 11 +++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/modules/common/src/test/scala/emil/AbstractSendTest.scala b/modules/common/src/test/scala/emil/AbstractSendTest.scala index e8b79d09..65bb98e9 100644 --- a/modules/common/src/test/scala/emil/AbstractSendTest.scala +++ b/modules/common/src/test/scala/emil/AbstractSendTest.scala @@ -50,7 +50,19 @@ abstract class AbstractSendTest[A] extends GreenmailTestSuite[A] { .unsafeRunSync() val actual = new String(mail2raw.toArray, StandardCharsets.UTF_8) val expected = - """------=_Part_1_272299100.1642183145458 + """Return-Path: + |Received: from ... (HELO ...); Thu Feb :: CET + |Date: Thu, Feb :: + (CET) + |From: joe@test.com + |To: joan@test.com + |Message-ID: <..@[...]> + |Subject: Hello! + |MIME-Version: . + |Content-Type: multipart/mixed; + | boundary="----=_Part__." + |User-Agent: my-email-client + | + |------=_Part_1_272299100.1642183145458 |Content-Type: multipart/alternative; | boundary="----=_Part_0_2060779434.1642183145447" | @@ -78,7 +90,14 @@ abstract class AbstractSendTest[A] extends GreenmailTestSuite[A] { |hello world! |------=_Part_1_272299100.1642183145458--""".stripMargin def normalize(s: String): String = - s.replaceAll("\\d+", "").replace("\r\n", "\n") + s.split("\n") + .filterNot(x => + x.startsWith("Received") || x.startsWith("Date") || x.startsWith("Message-ID") + ) + .mkString("\n") + .replaceAll("\\d+", "") + .replaceAll("\\s+", "") + .replace("\r\n", "\n") val actualNormalized = normalize(actual) val expectedNormalized = normalize(expected) assertEquals(actualNormalized, expectedNormalized) diff --git a/modules/javamail/src/main/scala/emil/javamail/conv/BodyDecode.scala b/modules/javamail/src/main/scala/emil/javamail/conv/BodyDecode.scala index 00d48ed3..974fe79f 100644 --- a/modules/javamail/src/main/scala/emil/javamail/conv/BodyDecode.scala +++ b/modules/javamail/src/main/scala/emil/javamail/conv/BodyDecode.scala @@ -1,7 +1,6 @@ package emil.javamail.conv -import java.io.ByteArrayOutputStream -import java.io.InputStream +import java.io.{ByteArrayOutputStream, InputStream} import java.nio.charset.Charset import cats.Applicative @@ -133,9 +132,11 @@ trait BodyDecode { Conv { msg => ThreadClassLoader { Util.withReadFolder(msg) { _ => - val in = msg.getRawInputStream - try BodyDecode.loadBytes(in) - finally in.close() + val out = new ByteArrayOutputStream() + try { + msg.writeTo(out) + ByteVector.view(out.toByteArray) + } finally out.close() } } } From ae053ac9457f793276f37d3e835af242da348340 Mon Sep 17 00:00:00 2001 From: Ondra Pelech Date: Mon, 7 Mar 2022 23:11:12 +0100 Subject: [PATCH 3/3] Catch exceptions from Conv#convert (#344) --- .../emil/javamail/internal/ops/LoadMail.scala | 17 +++++----- .../javamail/internal/ops/LoadMailRaw.scala | 32 +++++++++++++------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala index fa7b6b17..39ad9ade 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMail.scala @@ -1,6 +1,7 @@ package emil.javamail.internal.ops import cats.effect.Sync +import cats.syntax.all._ import emil._ import emil.javamail.conv.Conv import emil.javamail.internal.{JavaMailConnection, JavaMailImapConnection, Logger} @@ -14,39 +15,39 @@ object LoadMail { )(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailConnection, Option[Mail[F]]] = - FindMail[F](mh).map { optMime => + FindMail[F](mh).andThen { optMime => logger.debug(s"Loading complete mail for '$mh' from mime message '$optMime'") - optMime.map(cm.convert) + optMime.traverse(mime => Sync[F].delay(cm.convert(mime))) } def byUid[F[_]: Sync](folder: MailFolder, uid: MailUid)(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailImapConnection, Option[Mail[F]]] = - FindMail.byUid[F](folder, uid).map { optMime => + FindMail.byUid[F](folder, uid).andThen { optMime => logger .debug(s"Loaded complete raw mail for '$uid' from mime message '$optMime'") - optMime.map(cm.convert) + optMime.traverse(mime => Sync[F].delay(cm.convert(mime))) } def byUid[F[_]: Sync](folder: MailFolder, start: MailUid, end: MailUid)(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailImapConnection, List[Mail[F]]] = - FindMail.byUid[F](folder, start, end).map { mimes => + FindMail.byUid[F](folder, start, end).andThen { mimes => logger .debug( s"Loaded complete raw mail from '$start' to '$end' from mime messages: ${mimes.size}" ) - mimes.map(cm.convert) + mimes.traverse(mime => Sync[F].delay(cm.convert(mime))) } def byUid[F[_]: Sync](folder: MailFolder, uids: Set[MailUid])(implicit cm: Conv[MimeMessage, Mail[F]] ): MailOp[F, JavaMailImapConnection, List[Mail[F]]] = - FindMail.byUid[F](folder, uids).map { mimes => + FindMail.byUid[F](folder, uids).andThen { mimes => logger .debug( s"Loaded complete raw mail for '$uids' from mime messages: ${mimes.size}" ) - mimes.map(cm.convert) + mimes.traverse(mime => Sync[F].delay(cm.convert(mime))) } } diff --git a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala index 16d61217..1bed567b 100644 --- a/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala +++ b/modules/javamail/src/main/scala/emil/javamail/internal/ops/LoadMailRaw.scala @@ -1,6 +1,7 @@ package emil.javamail.internal.ops import cats.effect.Sync +import cats.syntax.all._ import emil._ import emil.javamail.conv.Conv import emil.javamail.internal.{JavaMailConnection, JavaMailImapConnection, Logger} @@ -15,43 +16,54 @@ object LoadMailRaw { )(implicit cm: Conv[MimeMessage, ByteVector] ): MailOp[F, JavaMailConnection, Option[ByteVector]] = - FindMail[F](mh).map { optMime => + FindMail[F](mh).andThen { optMime => logger.debug(s"Loading complete raw mail for '$mh' from mime message '$optMime'") - optMime.map(cm.convert) + optMime.traverse(mime => Sync[F].delay(cm.convert(mime))) } def byUid[F[_]: Sync](folder: MailFolder, uid: MailUid)(implicit cm: Conv[MimeMessage, MailHeader], cb: Conv[MimeMessage, ByteVector] ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = - FindMail.byUid[F](folder, uid).map { optMime => - logger - .debug(s"Loaded complete raw mail for '$uid' from mime message '$optMime'") - optMime.map(mime => cm.convert(mime) -> cb.convert(mime)).toList.toMap + FindMail.byUid[F](folder, uid).andThen { optMime => + logger.debug(s"Loaded complete raw mail for '$uid' from mime message '$optMime'") + optMime + .traverse { mime => + (Sync[F].delay(cm.convert(mime)), Sync[F].delay(cb.convert(mime))).tupled + } + .map(_.toList.toMap) } def byUid[F[_]: Sync](folder: MailFolder, start: MailUid, end: MailUid)(implicit cm: Conv[MimeMessage, MailHeader], cb: Conv[MimeMessage, ByteVector] ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = - FindMail.byUid[F](folder, start, end).map { mimes => + FindMail.byUid[F](folder, start, end).andThen { mimes => logger .debug( s"Loaded complete raw mail from '$start' to '$end' from mime messages: ${mimes.size}" ) - mimes.map(mime => cm.convert(mime) -> cb.convert(mime)).toMap + mimes + .traverse { mime => + (Sync[F].delay(cm.convert(mime)), Sync[F].delay(cb.convert(mime))).tupled + } + .map(_.toMap) } def byUid[F[_]: Sync](folder: MailFolder, uids: Set[MailUid])(implicit cm: Conv[MimeMessage, MailHeader], cb: Conv[MimeMessage, ByteVector] ): MailOp[F, JavaMailImapConnection, Map[MailHeader, ByteVector]] = - FindMail.byUid[F](folder, uids).map { mimes => + FindMail.byUid[F](folder, uids).andThen { mimes => logger .debug( s"Loaded complete raw mail for '$uids' from mime messages: ${mimes.size}" ) - mimes.map(mime => cm.convert(mime) -> cb.convert(mime)).toMap + mimes + .traverse { mime => + (Sync[F].delay(cm.convert(mime)), Sync[F].delay(cb.convert(mime))).tupled + } + .map(_.toMap) } }