<commit_before>package im.actor.server.activation.internal

import java.nio.file.{ Paths, Files }
import java.time.temporal.ChronoUnit._
import java.time.{ LocalDateTime, ZoneOffset }

import akka.actor._
import akka.pattern.ask
import akka.stream.Materializer
import akka.util.Timeout
import im.actor.server.activation.Activation.{ CallCode, Code, EmailCode, SmsCode }
import im.actor.server.activation._
import im.actor.server.email.{ EmailSender, Message }
import im.actor.server.models.AuthCode
import im.actor.server.persist
import im.actor.server.sms.{ AuthCallEngine, AuthSmsEngine }
import slick.driver.PostgresDriver.api._

import scala.concurrent.duration._
import scala.concurrent.{ ExecutionContext, Future }
import scalaz.{ -\/, \/, \/- }

object InternalCodeActivation {

  private[activation] sealed trait Message

  private[activation] final case class Send(code: Code)

  private[activation] case class SendAck(result: String \/ Unit)

  private[activation] final case class ForgetSentCode(code: Code) extends Message

  def newContext(config: ActivationConfig, smsEngine: AuthSmsEngine, callEngine: AuthCallEngine, emailSender: EmailSender)(
    implicit
    system:       ActorSystem,
    materializer: Materializer,
    db:           Database,
    ec:           ExecutionContext
  ): InternalCodeActivation = {
    new InternalCodeActivation(
      system.actorOf(Props(classOf[Activation], config.repeatLimit, smsEngine, callEngine, emailSender, materializer), "activation"),
      config
    )
  }
}

private[activation] class InternalCodeActivation(activationActor: ActorRef, config: ActivationConfig)(implicit db: Database, ec: ExecutionContext) extends CodeActivation {

  import InternalCodeActivation._
  import im.actor.server.activation.Activation._

  implicit val timeout: Timeout = Timeout(20.seconds)

  def send(transactionHash: Option[String], code: Code): DBIO[String \/ Unit] = (transactionHash match {
    case Some(hash) ⇒ for (_ ← persist.AuthCode.createOrUpdate(hash, code.code)) yield ()
    case None       ⇒ DBIO.successful(())
  }) flatMap (_ ⇒ DBIO.from(sendCode(code)))

  def validate(transactionHash: String, code: String): DBIO[ValidationResponse] =
    for {
      optCode ← persist.AuthCode.findByTransactionHash(transactionHash)
      result ← optCode map {
        case s if isExpired(s) ⇒
          for (_ ← persist.AuthCode.deleteByTransactionHash(transactionHash)) yield ExpiredCode
        case s if s.code != code ⇒
          if (s.attempts + 1 >= config.attempts) {
            for (_ ← persist.AuthCode.deleteByTransactionHash(transactionHash)) yield ExpiredCode
          } else {
            for (_ ← persist.AuthCode.incrementAttempts(transactionHash, s.attempts)) yield InvalidCode
          }
        case _ ⇒ DBIO.successful(Validated)
      } getOrElse DBIO.successful(InvalidHash)
    } yield result

  def finish(transactionHash: String): DBIO[Unit] = persist.AuthCode.deleteByTransactionHash(transactionHash).map(_ ⇒ ())

  private def isExpired(code: AuthCode): Boolean =
    code.createdAt.plus(config.expiration.toMillis, MILLIS).isBefore(LocalDateTime.now(ZoneOffset.UTC))

  private def sendCode(code: Code): Future[String \/ Unit] =
    code match {
      case p: PhoneCode if isTestPhone(p.phone) ⇒ Future.successful(\/-(()))
      case _                                    ⇒ (activationActor ? Send(code)).mapTo[SendAck].map(_.result)
    }

  private def isTestPhone(number: Long): Boolean = number.toString.startsWith("7555")
}

class Activation(repeatLimit: Duration, smsEngine: AuthSmsEngine, callEngine: AuthCallEngine, emailSender: EmailSender)(implicit materializer: Materializer) extends Actor with ActorLogging {

  import InternalCodeActivation._

  implicit val system = context.system
  implicit val ec = context.dispatcher

  private val sentCodes = new scala.collection.mutable.HashSet[Code]()

  private val emailTemplate = new String(Files.readAllBytes(Paths.get("mail-template.html")))

  def codeWasNotSent(code: Code) = !sentCodes.contains(code)

  def rememberSentCode(code: Code) = sentCodes += code

  def forgetSentCode(code: Code) = sentCodes -= code

  def forgetSentCodeAfterDelay(code: Code) =
    system.scheduler.scheduleOnce(repeatLimit.toMillis.millis, self, ForgetSentCode(code))

  override def receive: Receive = {
    case Send(code) ⇒
      val replyTo = sender()
      sendCode(code) foreach { resp ⇒ replyTo ! SendAck(resp) }
    case ForgetSentCode(code) ⇒ forgetSentCode(code)
  }

  private def sendCode(code: Code): Future[String \/ Unit] = {
    if (codeWasNotSent(code)) {<commit_msg>fix(server:activation):fix email activation template<commit_after>package im.actor.server.activation.internal

import java.nio.file.{ Paths, Files }
import java.time.temporal.ChronoUnit._
import java.time.{ LocalDateTime, ZoneOffset }

import akka.actor._
import akka.pattern.ask
import akka.stream.Materializer
import akka.util.Timeout
import im.actor.server.activation.Activation.{ CallCode, Code, EmailCode, SmsCode }
import im.actor.server.activation._
import im.actor.server.email.{ EmailSender, Message }
import im.actor.server.models.AuthCode
import im.actor.server.persist
import im.actor.server.sms.{ AuthCallEngine, AuthSmsEngine }
import slick.driver.PostgresDriver.api._

import scala.concurrent.duration._
import scala.concurrent.{ ExecutionContext, Future }
import scalaz.{ -\/, \/, \/- }

object InternalCodeActivation {

  private[activation] sealed trait Message

  private[activation] final case class Send(code: Code)

  private[activation] case class SendAck(result: String \/ Unit)

  private[activation] final case class ForgetSentCode(code: Code) extends Message

  def newContext(config: ActivationConfig, smsEngine: AuthSmsEngine, callEngine: AuthCallEngine, emailSender: EmailSender)(
    implicit
    system:       ActorSystem,
    materializer: Materializer,
    db:           Database,
    ec:           ExecutionContext
  ): InternalCodeActivation = {
    new InternalCodeActivation(
      system.actorOf(Props(classOf[Activation], config.repeatLimit, smsEngine, callEngine, emailSender, materializer), "activation"),
      config
    )
  }
}

private[activation] class InternalCodeActivation(activationActor: ActorRef, config: ActivationConfig)(implicit db: Database, ec: ExecutionContext) extends CodeActivation {

  import InternalCodeActivation._
  import im.actor.server.activation.Activation._

  implicit val timeout: Timeout = Timeout(20.seconds)

  def send(transactionHash: Option[String], code: Code): DBIO[String \/ Unit] = (transactionHash match {
    case Some(hash) ⇒ for (_ ← persist.AuthCode.createOrUpdate(hash, code.code)) yield ()
    case None       ⇒ DBIO.successful(())
  }) flatMap (_ ⇒ DBIO.from(sendCode(code)))

  def validate(transactionHash: String, code: String): DBIO[ValidationResponse] =
    for {
      optCode ← persist.AuthCode.findByTransactionHash(transactionHash)
      result ← optCode map {
        case s if isExpired(s) ⇒
          for (_ ← persist.AuthCode.deleteByTransactionHash(transactionHash)) yield ExpiredCode
        case s if s.code != code ⇒
          if (s.attempts + 1 >= config.attempts) {
            for (_ ← persist.AuthCode.deleteByTransactionHash(transactionHash)) yield ExpiredCode
          } else {
            for (_ ← persist.AuthCode.incrementAttempts(transactionHash, s.attempts)) yield InvalidCode
          }
        case _ ⇒ DBIO.successful(Validated)
      } getOrElse DBIO.successful(InvalidHash)
    } yield result

  def finish(transactionHash: String): DBIO[Unit] = persist.AuthCode.deleteByTransactionHash(transactionHash).map(_ ⇒ ())

  private def isExpired(code: AuthCode): Boolean =
    code.createdAt.plus(config.expiration.toMillis, MILLIS).isBefore(LocalDateTime.now(ZoneOffset.UTC))

  private def sendCode(code: Code): Future[String \/ Unit] =
    code match {
      case p: PhoneCode if isTestPhone(p.phone) ⇒ Future.successful(\/-(()))
      case _                                    ⇒ (activationActor ? Send(code)).mapTo[SendAck].map(_.result)
    }

  private def isTestPhone(number: Long): Boolean = number.toString.startsWith("7555")
}

class Activation(repeatLimit: Duration, smsEngine: AuthSmsEngine, callEngine: AuthCallEngine, emailSender: EmailSender)(implicit materializer: Materializer) extends Actor with ActorLogging {

  import InternalCodeActivation._

  implicit val system = context.system
  implicit val ec = context.dispatcher

  private val sentCodes = new scala.collection.mutable.HashSet[Code]()

  private val emailTemplate = new String(Files.readAllBytes(Paths.get("/mail-template.html")))

  def codeWasNotSent(code: Code) = !sentCodes.contains(code)

  def rememberSentCode(code: Code) = sentCodes += code

  def forgetSentCode(code: Code) = sentCodes -= code

  def forgetSentCodeAfterDelay(code: Code) =
    system.scheduler.scheduleOnce(repeatLimit.toMillis.millis, self, ForgetSentCode(code))

  override def receive: Receive = {
    case Send(code) ⇒
      val replyTo = sender()
      sendCode(code) foreach { resp ⇒ replyTo ! SendAck(resp) }
    case ForgetSentCode(code) ⇒ forgetSentCode(code)
  }

  private def sendCode(code: Code): Future[String \/ Unit] = {
    if (codeWasNotSent(code)) {
{'fraction_non_alphanumeric': 0.10557596714934082, 'fraction_numerical': 0.0015128593040847202, 'mean_word_length': 4.11608623548922, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4160049', 'n_tokens_mistral': 2783, 'n_tokens_neox': 2713, 'n_words': 651}