backend/app/extraction/email/olm/OlmMessage.scala (99 lines of code) (raw):
package extraction.email.olm
import model.{Email, Priority, Recipient, Uri}
import utils.HtmlToPlainText
import scala.xml.{Node, NodeSeq}
case class OlmMessage(
messageId: Option[String],
sender: Option[Recipient],
recipients: List[Recipient],
sentAt: Option[String],
priority: Option[String],
subject: String,
inReplyTo: List[String],
references: List[String],
body: Option[String],
html: Option[String],
attachments: List[OlmAttachment]
)
case class OlmAttachment(
name: String, // as it appears in the UI to the user
path: String, // where it actually is in the ZIP
contentId: Option[String], // if it is referenced inline in the email,
contentType: Option[String]
)
object OlmMessage {
implicit class RichNodeSeq(nodeSeq: NodeSeq) {
def textOption: Option[String] = Some(nodeSeq.text).filter(_.nonEmpty)
def \@?(attribute: String): Option[String] = Some(nodeSeq \@ attribute).filter(_.nonEmpty)
}
def apply(node: Node): OlmMessage = {
val maybeSender = (node \ "OPFMessageCopySenderAddress" \ "emailAddress").headOption
val maybeCopyFrom = (node \ "OPFMessageCopyFromAddresses" \ "emailAddress").headOption
val sender = (maybeSender orElse maybeCopyFrom).map(parseRecipient)
val copyToAddresses = (node \ "OPFMessageCopyToAddresses" \ "emailAddress").map(parseRecipient).toList
val copyCcAddresses = (node \ "OPFMessageCopyCCAddresses" \ "emailAddress").map(parseRecipient).toList
val copyBccAddresses = (node \ "OPFMessageCopyBCCAddresses" \ "emailAddress").map(parseRecipient).toList
val attachments = (node \ "OPFMessageCopyAttachmentList" \ "messageAttachment").map(OlmAttachment(_)).toList
val meetingAttachments = (node \ "OPFMessageCopyMeetingData").textOption.flatMap(OlmAttachment.meetingData).toList
OlmMessage(
messageId = (node \ "OPFMessageCopyMessageID").textOption,
sender = sender,
recipients = copyToAddresses ++ copyCcAddresses ++ copyBccAddresses,
sentAt = (node \ "OPFMessageCopySentTime").textOption,
priority = parsePriority(node),
subject = (node \ "OPFMessageCopySubject").text,
inReplyTo = (node \ "OPFMessageCopyInReplyTo").textOption.toList,
references = (node \ "OPFMessageCopyReferences").textOption.toList,
body = (node \ "OPFMessageCopyBody").textOption,
html = (node \ "OPFMessageCopyHTMLBody").textOption,
attachments = attachments ++ meetingAttachments
)
}
def parseRecipient(node: Node): Recipient = {
val emailAddress = node \@ "OPFContactEmailAddressAddress"
val displayName = node \@? "OPFContactEmailAddressName"
Recipient(displayName, emailAddress)
}
def parsePriority(node: Node): Option[String] = {
(node \ "OPFMessageGetPriority").textOption.map(_.trim.toInt).map {
case normal if normal == 3 => Priority.Normal
case urgent if urgent < 3 => Priority.Urgent
case notUrgent if notUrgent > 3 => Priority.NotUrgent
}
}
def toEmail(message: OlmMessage): Email = Email.createFrom(
maybeUri = message.messageId.map(Email.cleanUri).map(Uri.apply),
from = message.sender,
recipients = message.recipients,
sentAt = message.sentAt,
sensitivity = None, // TODO MRB: parse sensitivity (if it exists)
priority = message.priority,
subject = message.subject,
body = (message.body orElse message.html).map(HtmlToPlainText.convert).getOrElse("missing body"),
inReplyTo = message.inReplyTo.flatMap(Email.cleanInReplyTo),
references = message.references.flatMap(Email.cleanInReplyTo),
html = message.html,
attachmentCount = message.attachments.count(_.contentId.isEmpty)
)
}
object OlmAttachment {
import OlmMessage.RichNodeSeq
def apply(node: Node): OlmAttachment = OlmAttachment(
name = node \@ "OPFAttachmentName",
path = node \@ "OPFAttachmentURL",
contentId = node \@? "OPFAttachmentContentID",
contentType = node \@? "OPFAttachmentContentType"
)
def meetingData(path: String): Option[OlmAttachment] = {
if(path.endsWith(".ics")) {
Some(OlmAttachment(
name = "meeting.ics",
path = path,
contentId = None,
contentType = Some("text/calendar")
))
} else {
// It seems that they are always .ics but better safe than sorry
None
}
}
}