Advertisement
Guest User

gmail actor

a guest
Mar 29th, 2015
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 5.99 KB | None | 0 0
  1. package com.rastech.meta.google
  2.  
  3. import akka.actor._
  4. import akka.event.slf4j.Logger
  5. import akka.pattern.ask
  6. import akka.util.Timeout
  7. import scala.annotation.tailrec
  8. import scala.concurrent._
  9. import scala.concurrent.duration._
  10. import scala.util.{Try, Success, Failure}
  11. import spray.client.pipelining._
  12. import spray.http._
  13. import spray.http.ContentTypes._
  14. import spray.http.HttpHeaders._
  15. import spray.http.HttpMethods._
  16. import spray.http.MediaTypes._
  17. import spray.http.Uri._
  18. import spray.httpx.SprayJsonSupport._
  19. import spray.httpx.marshalling._
  20. import spray.json._
  21. import spray.routing.HttpServiceActor
  22.  
  23. import com.google.code.samples.oauth2.OAuth2Authenticator
  24. import com.sun.mail.imap.IMAPFolder
  25. import javax.mail.{Message, Multipart, BodyPart, Part, AuthenticationFailedException}
  26. import java.io.{InputStream, File}
  27.  
  28. import com.rastech.meta.util.GrizzledAkkaLogging
  29.  
  30. case object Start
  31.  
  32. class GmailUserActor(gmailAuthToken: String,
  33.                      email: String,
  34.                      metaId: String) extends Actor with GrizzledAkkaLogging {
  35.   // This may be not good security wise, using personal emails in log messages
  36.   val logName = email
  37.   val imapPort = 993
  38.   val gmailImapServerUrl = "imap.gmail.com"
  39.   import context.dispatcher
  40.  
  41.   // Google cancel's idles after 8 minutes without error
  42.   val idleDuration = 5.minutes
  43.  
  44.   def receive: PartialFunction[Any, Unit] = {
  45.     case Start => {
  46.       Try{
  47.           OAuth2Authenticator.connectToImap(gmailImapServerUrl,
  48.                                             imapPort,
  49.                                             email,
  50.                                             gmailAuthToken,
  51.                                             true)
  52.       } match {
  53.         case Success(imapStore) => {
  54.           val inbox = imapStore.getFolder("inbox").asInstanceOf[IMAPFolder]
  55.           process(Set(), inbox) //TODO process should return a Try/Failure
  56.         }
  57.         case Failure(ex) => ex match {
  58.           case afe: AuthenticationFailedException => {
  59.             log.error(s"Could not authenticate user info: $ex")
  60.             // TODO: send error back to application layer
  61.           }
  62.           case ex => {
  63.             log.error(s"There was an error trying to connect to the folder: $ex")
  64.           }
  65.         }
  66.       }
  67.  
  68.       // Infinite loop to watch and process all the messages in a IMAPFolder
  69.       // This annotation makes the compiler emit a warning if the following recursive function
  70.       // can't have the tailrecursion optimization applied to it
  71.       @tailrec
  72.       def process(processedMessages: Set[Message], folder: IMAPFolder): Unit = {
  73.         if(!folder.isOpen()){
  74.           folder.open(javax.mail.Folder.READ_ONLY)
  75.         }
  76.         // Google cancels idle after 8 minutes but the folder.idle keeps blocking, so this
  77.         // Await constructs makes it time out after the idleDuration amount of time
  78.         // See: http://stackoverflow.com/a/16004749/3149857
  79.         Try{Await.result(Future{folder.idle(true)}, idleDuration)} match {
  80.           case Success(()) => { // Dat unit
  81.             log.debug(s"Idle did not return before $idleDuration")
  82.             val allMessages = folder.getMessages.toSet
  83.             val newMessages = allMessages diff processedMessages
  84.             val newAttachments = newMessages.flatMap(getMessageAttachments)
  85.             newAttachments.foreach{ case Attachment(name, is) =>
  86.               log.debug(s"Found attachment with name $name")
  87.               processAttachment(name, is)
  88.             }
  89.             process(allMessages, folder)
  90.           }
  91.           case Failure(_) => {
  92.             log.debug(s"Idle did not return before $idleDuration")
  93.             process(processedMessages, folder)
  94.           }
  95.         }
  96.       }
  97.     }
  98.   }
  99.  
  100.   private case class Attachment(filename: String, inputStream: InputStream)
  101.  
  102.   // Provides the nice collection api stuff for the bodyparts of a Multipart object
  103.   private case class BodyPartTraverser(mp: Multipart) extends Traversable[BodyPart] {
  104.     def foreach[U](f: BodyPart => U): Unit = {
  105.       for (i <- 0 until mp.getCount) {
  106.         f(mp.getBodyPart(i))
  107.       }
  108.     }
  109.   }
  110.  
  111.   // Scala version of javamail code to get all attachements for a message
  112.   // See: http://stackoverflow.com/a/18228443/3149857
  113.   private def getMessageAttachments(message: Message): List[Attachment] = {
  114.     message.getContent match {
  115.       case multipart: Multipart => {
  116.         getAttachments(List(), BodyPartTraverser(multipart).toList)
  117.       }
  118.       // Only multipart messages have attachments
  119.       case _ => List()
  120.     }
  121.   }
  122.  
  123.   @tailrec
  124.   private def getAttachments(attachments: List[Attachment],
  125.                              parts: List[BodyPart]): List[Attachment] = {
  126.     parts match {
  127.       case Nil => attachments
  128.       case part :: remainingParts => {
  129.         part.getContent match {
  130.           // Syntax for if either type matches
  131.           case (_ : InputStream | _ : String) => {
  132.             // These parts represent actual attachements
  133.             // TODO: Use some option monad magic to make this cleaner
  134.             val fileNameBlank = Option(part.getFileName).map{ _.trim.isEmpty }.getOrElse(true)
  135.             if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition) || !fileNameBlank ) {
  136.               val attachment = Attachment(part.getFileName, part.getInputStream)
  137.               getAttachments(attachment :: attachments, remainingParts)
  138.             } else {
  139.               getAttachments(attachments, remainingParts)
  140.             }
  141.           }
  142.           case multipart: Multipart => {
  143.             // This part represenets more parts that might have attachments themselves
  144.             // TODO: Find a collection type that has better append/++ performance
  145.             getAttachments(attachments, remainingParts ++ BodyPartTraverser(multipart))
  146.           }
  147.           case _ => {
  148.             // Any other part isn't an attachment and can be ignored
  149.             getAttachments(attachments, remainingParts)
  150.           }
  151.         }
  152.       }
  153.     }
  154.   }
  155. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement