Skip to content

Commit

Permalink
Support for Gmail labels (#329)
Browse files Browse the repository at this point in the history
* Gmail

* Gmail 2

* Gmail

* Gmail

* fix

* smtp
  • Loading branch information
sideeffffect authored Feb 5, 2022
1 parent e3edf49 commit 089a17d
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 48 deletions.
17 changes: 13 additions & 4 deletions modules/common/src/main/scala/emil/AccessImap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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)

}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package emil.javamail.internal

case class GmailLabel(value: String)
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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])
}
}
}
Expand All @@ -37,30 +37,31 @@ 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
}
}
}

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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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")
)
}

}
4 changes: 3 additions & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 089a17d

Please sign in to comment.