src/Pure/General/mail.scala
author wenzelm
Mon, 02 Dec 2024 22:16:29 +0100
changeset 81541 5335b1ca6233
parent 81287 f7d7a6a4f857
permissions -rw-r--r--
more elementary operation Term.variant_bounds: only for bounds vs. frees, no consts, no tfrees;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     1
/*  Title:      Pure/General/mail.scala
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     2
    Author:     Fabian Huch, TU Muenchen
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     3
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     4
Support for sending text mails via SMTP.
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     5
*/
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     6
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     7
package isabelle
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     8
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
     9
78855
6fdcd6c8c97a prefer old-style import "=>";
wenzelm
parents: 78829
diff changeset
    10
import java.util.{Properties => JProperties}
79443
0d7c7fe65638 update javamail component with current jakarta mail APIs and eclipse angus implementation;
Fabian Huch <huch@in.tum.de>
parents: 78855
diff changeset
    11
import jakarta.mail.internet.{InternetAddress, MimeMessage}
0d7c7fe65638 update javamail component with current jakarta mail APIs and eclipse angus implementation;
Fabian Huch <huch@in.tum.de>
parents: 78855
diff changeset
    12
import jakarta.mail.{AuthenticationFailedException, Authenticator, Message, MessagingException,
78855
6fdcd6c8c97a prefer old-style import "=>";
wenzelm
parents: 78829
diff changeset
    13
  PasswordAuthentication, Transport as JTransport, Session => JSession}
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    14
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    15
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    16
object Mail {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    17
  /* validated addresses */
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    18
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    19
  final class Address private[Mail](rep: String) {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    20
    override def toString: String = rep
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    21
  }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    22
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    23
  val default_address: Address = address("user@localhost")
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    24
  def address(s: String): Address =
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    25
    Exn.capture(new InternetAddress(s).validate()) match {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    26
      case Exn.Res(_) => new Address(s)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    27
      case _ => error("Invalid mail address: " + quote(s))
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    28
    }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    29
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    30
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    31
  /* smtp server */
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    32
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    33
  enum Transport {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    34
    case Plaintext extends Transport
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    35
    case SSL(protocol: String = "TLSv1.2") extends Transport
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    36
    case STARTTLS extends Transport
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    37
  }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    38
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    39
  class Server (
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    40
    sender: Address,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    41
    smtp_host: String,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    42
    smtp_port: Int = 587,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    43
    user: String = "",
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    44
    password: String = "",
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    45
    transport: Transport = Transport.SSL()
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    46
  ) {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    47
    def use_auth: Boolean = user.nonEmpty && password.nonEmpty
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    48
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    49
    private def mail_session: JSession = {
79979
wenzelm
parents: 79444
diff changeset
    50
      val props = new JProperties
79444
71fde9e76ca9 proper SMTP session: set envelope sender address correctly;
Fabian Huch <huch@in.tum.de>
parents: 79443
diff changeset
    51
      props.setProperty("mail.smtp.from", sender.toString)
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    52
      props.setProperty("mail.smtp.host", smtp_host)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    53
      props.setProperty("mail.smtp.port", smtp_port.toString)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    54
      props.setProperty("mail.smtp.auth", use_auth.toString)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    55
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    56
      transport match {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    57
        case Transport.SSL(protocol) =>
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    58
          props.setProperty("mail.smtp.ssl.enable", "true")
79979
wenzelm
parents: 79444
diff changeset
    59
          props.setProperty("mail.smtp.ssl.protocols", protocol)
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    60
        case Transport.STARTTLS =>
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    61
          props.setProperty("mail.smtp.starttls.enable", "true")
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    62
        case Transport.Plaintext =>
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    63
      }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    64
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    65
      val authenticator = new Authenticator() {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    66
        override def getPasswordAuthentication = new PasswordAuthentication(user, password)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    67
      }
79444
71fde9e76ca9 proper SMTP session: set envelope sender address correctly;
Fabian Huch <huch@in.tum.de>
parents: 79443
diff changeset
    68
      JSession.getInstance(props, authenticator)
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    69
    }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    70
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    71
    def defined: Boolean = smtp_host.nonEmpty
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    72
    def check(): Unit = {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    73
      val transport = mail_session.getTransport("smtp")
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    74
      try {
81287
f7d7a6a4f857 proper passwordless smtp check: must be null;
Fabian Huch <huch@in.tum.de>
parents: 79979
diff changeset
    75
        transport.connect(smtp_host, smtp_port,
f7d7a6a4f857 proper passwordless smtp check: must be null;
Fabian Huch <huch@in.tum.de>
parents: 79979
diff changeset
    76
          if (user.nonEmpty) user else null, if (password.nonEmpty) password else null)
78829
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    77
        transport.close()
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    78
      }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    79
      catch {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    80
        case exn: Throwable => error("Could not connect to SMTP server: " + exn.getMessage)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    81
      }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    82
    }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    83
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    84
    def send(mail: Mail): Unit = {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    85
      val from_address = mail.from_address.getOrElse(sender)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    86
      val from =
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    87
        if (mail.from_name.isEmpty) new InternetAddress(from_address.toString)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    88
        else new InternetAddress(from_address.toString, mail.from_name)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    89
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    90
      val message = new MimeMessage(mail_session)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    91
      message.setFrom(from)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    92
      message.setSender(new InternetAddress(sender.toString))
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    93
      message.setSubject(mail.subject)
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    94
      message.setText(mail.content, "UTF-8")
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    95
      message.setSentDate(new java.util.Date())
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    96
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    97
      for (recipient <- mail.recipients) {
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    98
        message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient.toString))
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
    99
      }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   100
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   101
      try { JTransport.send(message) }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   102
      catch { case exn: Throwable => error("Sending mail failed: " + exn.getMessage) }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   103
    }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   104
  }
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   105
}
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   106
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   107
case class Mail(
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   108
  subject: String,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   109
  recipients: List[Mail.Address],
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   110
  content: String,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   111
  from_address: Option[Mail.Address] = None,
58315c8a5cc4 added mail module;
Fabian Huch <huch@in.tum.de>
parents:
diff changeset
   112
  from_name: String = "")