author | wenzelm |
Mon, 02 Dec 2024 22:16:29 +0100 | |
changeset 81541 | 5335b1ca6233 |
parent 81287 | f7d7a6a4f857 |
permissions | -rw-r--r-- |
78829 | 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 |
||
78855 | 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 | 13 |
PasswordAuthentication, Transport as JTransport, Session => JSession} |
78829 | 14 |
|
15 |
||
16 |
object Mail { |
|
17 |
/* validated addresses */ |
|
18 |
||
19 |
final class Address private[Mail](rep: String) { |
|
20 |
override def toString: String = rep |
|
21 |
} |
|
22 |
||
23 |
val default_address: Address = address("user@localhost") |
|
24 |
def address(s: String): Address = |
|
25 |
Exn.capture(new InternetAddress(s).validate()) match { |
|
26 |
case Exn.Res(_) => new Address(s) |
|
27 |
case _ => error("Invalid mail address: " + quote(s)) |
|
28 |
} |
|
29 |
||
30 |
||
31 |
/* smtp server */ |
|
32 |
||
33 |
enum Transport { |
|
34 |
case Plaintext extends Transport |
|
35 |
case SSL(protocol: String = "TLSv1.2") extends Transport |
|
36 |
case STARTTLS extends Transport |
|
37 |
} |
|
38 |
||
39 |
class Server ( |
|
40 |
sender: Address, |
|
41 |
smtp_host: String, |
|
42 |
smtp_port: Int = 587, |
|
43 |
user: String = "", |
|
44 |
password: String = "", |
|
45 |
transport: Transport = Transport.SSL() |
|
46 |
) { |
|
47 |
def use_auth: Boolean = user.nonEmpty && password.nonEmpty |
|
48 |
||
49 |
private def mail_session: JSession = { |
|
79979 | 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 | 52 |
props.setProperty("mail.smtp.host", smtp_host) |
53 |
props.setProperty("mail.smtp.port", smtp_port.toString) |
|
54 |
props.setProperty("mail.smtp.auth", use_auth.toString) |
|
55 |
||
56 |
transport match { |
|
57 |
case Transport.SSL(protocol) => |
|
58 |
props.setProperty("mail.smtp.ssl.enable", "true") |
|
79979 | 59 |
props.setProperty("mail.smtp.ssl.protocols", protocol) |
78829 | 60 |
case Transport.STARTTLS => |
61 |
props.setProperty("mail.smtp.starttls.enable", "true") |
|
62 |
case Transport.Plaintext => |
|
63 |
} |
|
64 |
||
65 |
val authenticator = new Authenticator() { |
|
66 |
override def getPasswordAuthentication = new PasswordAuthentication(user, password) |
|
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 | 69 |
} |
70 |
||
71 |
def defined: Boolean = smtp_host.nonEmpty |
|
72 |
def check(): Unit = { |
|
73 |
val transport = mail_session.getTransport("smtp") |
|
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 | 77 |
transport.close() |
78 |
} |
|
79 |
catch { |
|
80 |
case exn: Throwable => error("Could not connect to SMTP server: " + exn.getMessage) |
|
81 |
} |
|
82 |
} |
|
83 |
||
84 |
def send(mail: Mail): Unit = { |
|
85 |
val from_address = mail.from_address.getOrElse(sender) |
|
86 |
val from = |
|
87 |
if (mail.from_name.isEmpty) new InternetAddress(from_address.toString) |
|
88 |
else new InternetAddress(from_address.toString, mail.from_name) |
|
89 |
||
90 |
val message = new MimeMessage(mail_session) |
|
91 |
message.setFrom(from) |
|
92 |
message.setSender(new InternetAddress(sender.toString)) |
|
93 |
message.setSubject(mail.subject) |
|
94 |
message.setText(mail.content, "UTF-8") |
|
95 |
message.setSentDate(new java.util.Date()) |
|
96 |
||
97 |
for (recipient <- mail.recipients) { |
|
98 |
message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient.toString)) |
|
99 |
} |
|
100 |
||
101 |
try { JTransport.send(message) } |
|
102 |
catch { case exn: Throwable => error("Sending mail failed: " + exn.getMessage) } |
|
103 |
} |
|
104 |
} |
|
105 |
} |
|
106 |
||
107 |
case class Mail( |
|
108 |
subject: String, |
|
109 |
recipients: List[Mail.Address], |
|
110 |
content: String, |
|
111 |
from_address: Option[Mail.Address] = None, |
|
112 |
from_name: String = "") |