|
1 /* Title: Pure/General/mail.scala |
|
2 Author: Fabian Huch, TU Muenchen |
|
3 |
|
4 Support for sending text mails via SMTP. |
|
5 */ |
|
6 |
|
7 package isabelle |
|
8 |
|
9 |
|
10 import java.util.Properties as JProperties |
|
11 import javax.mail.internet.{InternetAddress, MimeMessage} |
|
12 import javax.mail.{AuthenticationFailedException, Authenticator, Message, MessagingException, PasswordAuthentication, Transport as JTransport, Session as JSession} |
|
13 |
|
14 |
|
15 object Mail { |
|
16 /* validated addresses */ |
|
17 |
|
18 final class Address private[Mail](rep: String) { |
|
19 override def toString: String = rep |
|
20 } |
|
21 |
|
22 val default_address: Address = address("user@localhost") |
|
23 def address(s: String): Address = |
|
24 Exn.capture(new InternetAddress(s).validate()) match { |
|
25 case Exn.Res(_) => new Address(s) |
|
26 case _ => error("Invalid mail address: " + quote(s)) |
|
27 } |
|
28 |
|
29 |
|
30 /* smtp server */ |
|
31 |
|
32 enum Transport { |
|
33 case Plaintext extends Transport |
|
34 case SSL(protocol: String = "TLSv1.2") extends Transport |
|
35 case STARTTLS extends Transport |
|
36 } |
|
37 |
|
38 class Server ( |
|
39 sender: Address, |
|
40 smtp_host: String, |
|
41 smtp_port: Int = 587, |
|
42 user: String = "", |
|
43 password: String = "", |
|
44 transport: Transport = Transport.SSL() |
|
45 ) { |
|
46 def use_auth: Boolean = user.nonEmpty && password.nonEmpty |
|
47 |
|
48 private def mail_session: JSession = { |
|
49 val props = new JProperties() |
|
50 props.setProperty("mail.smtp.host", smtp_host) |
|
51 props.setProperty("mail.smtp.port", smtp_port.toString) |
|
52 props.setProperty("mail.smtp.auth", use_auth.toString) |
|
53 |
|
54 transport match { |
|
55 case Transport.SSL(protocol) => |
|
56 props.setProperty("mail.smtp.ssl.enable", "true") |
|
57 props.setProperty("mail.smtp.ssl.protocols", protocol) |
|
58 case Transport.STARTTLS => |
|
59 props.setProperty("mail.smtp.starttls.enable", "true") |
|
60 case Transport.Plaintext => |
|
61 } |
|
62 |
|
63 val authenticator = new Authenticator() { |
|
64 override def getPasswordAuthentication = new PasswordAuthentication(user, password) |
|
65 } |
|
66 JSession.getDefaultInstance(props, authenticator) |
|
67 } |
|
68 |
|
69 def defined: Boolean = smtp_host.nonEmpty |
|
70 def check(): Unit = { |
|
71 val transport = mail_session.getTransport("smtp") |
|
72 try { |
|
73 transport.connect(smtp_host, smtp_port, user, password) |
|
74 transport.close() |
|
75 } |
|
76 catch { |
|
77 case exn: Throwable => error("Could not connect to SMTP server: " + exn.getMessage) |
|
78 } |
|
79 } |
|
80 |
|
81 def send(mail: Mail): Unit = { |
|
82 val from_address = mail.from_address.getOrElse(sender) |
|
83 val from = |
|
84 if (mail.from_name.isEmpty) new InternetAddress(from_address.toString) |
|
85 else new InternetAddress(from_address.toString, mail.from_name) |
|
86 |
|
87 val message = new MimeMessage(mail_session) |
|
88 message.setFrom(from) |
|
89 message.setSender(new InternetAddress(sender.toString)) |
|
90 message.setSubject(mail.subject) |
|
91 message.setText(mail.content, "UTF-8") |
|
92 message.setSentDate(new java.util.Date()) |
|
93 |
|
94 for (recipient <- mail.recipients) { |
|
95 message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient.toString)) |
|
96 } |
|
97 |
|
98 try { JTransport.send(message) } |
|
99 catch { case exn: Throwable => error("Sending mail failed: " + exn.getMessage) } |
|
100 } |
|
101 } |
|
102 } |
|
103 |
|
104 case class Mail( |
|
105 subject: String, |
|
106 recipients: List[Mail.Address], |
|
107 content: String, |
|
108 from_address: Option[Mail.Address] = None, |
|
109 from_name: String = "") |